package main

import (
	"bufio"
	"fmt"
	"math"
	"math/big"
	"os"
	"strings"
	"unicode"
)

// Built-in arithmetic functions

func builtinAdd(args []*Value, env *Environment) (*Value, error) {
	if len(args) == 0 {
		return NewInteger(0), nil
	}

	// Start with first argument
	result := args[0]
	if !result.IsNumber() {
		return nil, fmt.Errorf("+ expects numbers, got %v", result.Type)
	}

	// Add remaining arguments
	for i := 1; i < len(args); i++ {
		arg := args[i]
		if !arg.IsNumber() {
			return nil, fmt.Errorf("+ expects numbers, got %v", arg.Type)
		}
		var err error
		result, err = addNumbers(result, arg)
		if err != nil {
			return nil, err
		}
	}
	return result, nil
}

func builtinSub(args []*Value, env *Environment) (*Value, error) {
	if len(args) == 0 {
		return nil, fmt.Errorf("- expects at least one argument")
	}

	if len(args) == 1 {
		// Unary minus: negate the number
		arg := args[0]
		if !arg.IsNumber() {
			return nil, fmt.Errorf("- expects numbers")
		}
		return negateNumber(arg), nil
	}

	// Binary and n-ary subtraction
	result := args[0]
	if !result.IsNumber() {
		return nil, fmt.Errorf("- expects numbers")
	}

	for i := 1; i < len(args); i++ {
		arg := args[i]
		if !arg.IsNumber() {
			return nil, fmt.Errorf("- expects numbers")
		}
		var err error
		result, err = subtractNumbers(result, arg)
		if err != nil {
			return nil, err
		}
	}
	return result, nil
}

func builtinMul(args []*Value, env *Environment) (*Value, error) {
	if len(args) == 0 {
		return NewInteger(1), nil
	}

	// Start with first argument
	result := args[0]
	if !result.IsNumber() {
		return nil, fmt.Errorf("* expects numbers")
	}

	// Multiply remaining arguments
	for i := 1; i < len(args); i++ {
		arg := args[i]
		if !arg.IsNumber() {
			return nil, fmt.Errorf("* expects numbers")
		}
		var err error
		result, err = multiplyNumbers(result, arg)
		if err != nil {
			return nil, err
		}
	}
	return result, nil
}

func builtinDiv(args []*Value, env *Environment) (*Value, error) {
	if len(args) == 0 {
		return nil, fmt.Errorf("/ expects at least one argument")
	}

	if len(args) == 1 {
		// Reciprocal: 1/x
		arg := args[0]
		if !arg.IsNumber() {
			return nil, fmt.Errorf("/  expects numbers")
		}
		return reciprocalNumber(arg)
	}

	// Binary and n-ary division
	result := args[0]
	if !result.IsNumber() {
		return nil, fmt.Errorf("/ expects numbers")
	}

	for i := 1; i < len(args); i++ {
		arg := args[i]
		if !arg.IsNumber() {
			return nil, fmt.Errorf("/ expects numbers")
		}
		var err error
		result, err = divideNumbers(result, arg)
		if err != nil {
			return nil, err
		}
	}
	return result, nil
}

// Built-in comparison functions

func builtinEqual(args []*Value, env *Environment) (*Value, error) {
	if len(args) != 2 {
		return nil, fmt.Errorf("= expects exactly 2 arguments")
	}

	if !args[0].IsNumber() || !args[1].IsNumber() {
		return nil, fmt.Errorf("= expects numbers")
	}

	return NewBoolean(args[0].NumericEqual(args[1])), nil
}

func builtinLess(args []*Value, env *Environment) (*Value, error) {
	if len(args) != 2 {
		return nil, fmt.Errorf("< expects exactly 2 arguments")
	}

	if !args[0].IsNumber() || !args[1].IsNumber() {
		return nil, fmt.Errorf("< expects numbers")
	}

	cmp, err := compareNumbers(args[0], args[1])
	if err != nil {
		return nil, err
	}
	return NewBoolean(cmp < 0), nil
}

func builtinGreater(args []*Value, env *Environment) (*Value, error) {
	if len(args) != 2 {
		return nil, fmt.Errorf("> expects exactly 2 arguments")
	}

	if !args[0].IsNumber() || !args[1].IsNumber() {
		return nil, fmt.Errorf("> expects numbers")
	}

	cmp, err := compareNumbers(args[0], args[1])
	if err != nil {
		return nil, err
	}
	return NewBoolean(cmp > 0), nil
}

func builtinLessEqual(args []*Value, env *Environment) (*Value, error) {
	if len(args) != 2 {
		return nil, fmt.Errorf("<= expects exactly 2 arguments")
	}

	if !args[0].IsNumber() || !args[1].IsNumber() {
		return nil, fmt.Errorf("<= expects numbers")
	}

	cmp, err := compareNumbers(args[0], args[1])
	if err != nil {
		return nil, err
	}
	return NewBoolean(cmp <= 0), nil
}

func builtinGreaterEqual(args []*Value, env *Environment) (*Value, error) {
	if len(args) != 2 {
		return nil, fmt.Errorf(">= expects exactly 2 arguments")
	}

	if !args[0].IsNumber() || !args[1].IsNumber() {
		return nil, fmt.Errorf(">= expects numbers")
	}

	cmp, err := compareNumbers(args[0], args[1])
	if err != nil {
		return nil, err
	}
	return NewBoolean(cmp >= 0), nil
}

// Built-in logical functions

func builtinAnd(args []*Value, env *Environment) (*Value, error) {
	for _, arg := range args {
		if !arg.IsTruthy() {
			return NewBoolean(false), nil
		}
	}
	return NewBoolean(true), nil
}

func builtinOr(args []*Value, env *Environment) (*Value, error) {
	for _, arg := range args {
		if arg.IsTruthy() {
			return NewBoolean(true), nil
		}
	}
	return NewBoolean(false), nil
}

func builtinNot(args []*Value, env *Environment) (*Value, error) {
	if len(args) != 1 {
		return nil, fmt.Errorf("not expects exactly 1 argument")
	}

	if args[0] == nil {
		return NewBoolean(true), nil // nil is falsy, so (not nil) is true
	}

	return NewBoolean(!args[0].IsTruthy()), nil
}

// Built-in list functions

// builtinListp implements the list? predicate
// (list? obj) returns #t if obj is a list, #f otherwise
// In Scheme, a list is either the empty list or a pair whose cdr is a list
func builtinListp(args []*Value, env *Environment) (*Value, error) {
	if len(args) != 1 {
		return nil, fmt.Errorf("list? expects exactly 1 argument")
	}
	arg := args[0]
	
	// Empty list is a list
	if arg.Type == TypeNil || (arg.Type == TypeList && len(arg.List) == 0) {
		return NewBoolean(true), nil
	}
	
	// Check if it's a proper list
	seen := make(map[*Value]bool)
	current := arg
	
	for {
		// If we've seen this value before, we have a circular list
		if seen[current] {
			return NewBoolean(false), nil
		}
		seen[current] = true
		
		// Check if current is a pair
		if current.Type == TypePair {
			current = current.Cdr
		} else if current.Type == TypeList {
			// Handle TypeList representation
			if len(current.List) == 0 {
				return NewBoolean(true), nil
			}
			// Convert TypeList to a proper pair chain for checking
			// This is a simplified check for proper list
			return NewBoolean(true), nil
		} else if current.Type == TypeNil {
			// Reached the end of a proper list
			return NewBoolean(true), nil
		} else {
			// Not a pair or nil, not a proper list
			return NewBoolean(false), nil
		}
	}
}

// builtinLength implements the length procedure
// (length list) returns the number of elements in list
func builtinLength(args []*Value, env *Environment) (*Value, error) {
	if len(args) != 1 {
		return nil, fmt.Errorf("length expects exactly 1 argument")
	}
	arg := args[0]
	
	// Empty list has length 0
	if arg.Type == TypeNil || (arg.Type == TypeList && len(arg.List) == 0) {
		return NewInteger(0), nil
	}
	
	// Handle TypeList directly
	if arg.Type == TypeList {
		return NewInteger(int64(len(arg.List))), nil
	}
	
	// For pairs, count elements until we reach nil
	if arg.Type == TypePair {
		count := int64(0)
		seen := make(map[*Value]bool)
		current := arg
		
		for {
			// Check for circular lists
			if seen[current] {
				return nil, fmt.Errorf("length: circular list detected")
			}
			seen[current] = true
			
			count++
			current = current.Cdr
			
			// Check if we've reached the end
			if current.Type == TypeNil {
				return NewInteger(count), nil
			} else if current.Type == TypeList && len(current.List) == 0 {
				return NewInteger(count), nil
			} else if current.Type != TypePair && current.Type != TypeList {
				// Not a proper list
				return nil, fmt.Errorf("length: argument is not a list")
			} else if current.Type == TypeList {
				// Handle mixed TypeList at the end
				return NewInteger(count + int64(len(current.List))), nil
			}
		}
	}
	
	return nil, fmt.Errorf("length: argument is not a list")
}

// builtinMemq implements the memq procedure
// (memq obj list) returns the first sublist of list whose car is obj,
// where the sublists are determined by cdr and obj is compared using eq?
func builtinMemq(args []*Value, env *Environment) (*Value, error) {
	if len(args) != 2 {
		return nil, fmt.Errorf("memq expects exactly 2 arguments")
	}
	return mem(args[0], args[1], builtinEqp, env)
}

// builtinMemv implements the memv procedure
// (memv obj list) returns the first sublist of list whose car is obj,
// where the sublists are determined by cdr and obj is compared using eqv?
func builtinMemv(args []*Value, env *Environment) (*Value, error) {
	if len(args) != 2 {
		return nil, fmt.Errorf("memv expects exactly 2 arguments")
	}
	return mem(args[0], args[1], builtinEqvp, env)
}

// builtinMember implements the member procedure
// (member obj list) returns the first sublist of list whose car is obj,
// where the sublists are determined by cdr and obj is compared using equal?
func builtinMember(args []*Value, env *Environment) (*Value, error) {
	if len(args) != 2 {
		return nil, fmt.Errorf("member expects exactly 2 arguments")
	}
	return mem(args[0], args[1], builtinEqualp, env)
}

// Helper function for memq, memv, member
func mem(obj, list *Value, compare func([]*Value, *Environment) (*Value, error), env *Environment) (*Value, error) {
	// Check if second argument is a list
	if list.Type != TypeList && list.Type != TypeNil && list.Type != TypePair {
		return nil, fmt.Errorf("second argument must be a list")
	}
	
	// Empty list returns #f
	if list.Type == TypeNil || (list.Type == TypeList && len(list.List) == 0) {
		return NewBoolean(false), nil
	}
	
	// Handle TypeList
	if list.Type == TypeList {
		for i, elem := range list.List {
			result, err := compare([]*Value{obj, elem}, env)
			if err != nil {
				return nil, err
			}
			if result.Boolean {
				// Return the sublist starting from this element
				return NewList(list.List[i:]), nil
			}
		}
		return NewBoolean(false), nil
	}
	
	// Handle TypePair (and potentially dotted lists)
	current := list
	seen := make(map[*Value]bool)
	
	for {
		// Check for circular lists
		if seen[current] {
			return nil, fmt.Errorf("circular list detected")
		}
		seen[current] = true
		
		// Compare the current car with obj
		result, err := compare([]*Value{obj, current.Car}, env)
		if err != nil {
			return nil, err
		}
		if result.Boolean {
			return current, nil
		}
		
		// Move to next element
		current = current.Cdr
		
		// Check if we've reached the end
		if current.Type == TypeNil || (current.Type == TypeList && len(current.List) == 0) {
			return NewBoolean(false), nil
		}
		if current.Type != TypePair && current.Type != TypeList {
			// Dotted list with non-list at the end
			return NewBoolean(false), nil
		}
		if current.Type == TypeList {
			// Handle mixed TypeList at the end
			for _, elem := range current.List {
				result, err := compare([]*Value{obj, elem}, env)
				if err != nil {
					return nil, err
				}
				if result.Boolean {
					return current, nil
				}
			}
			return NewBoolean(false), nil
		}
	}
}

// builtinAssq implements the assq procedure
// (assq obj alist) returns the first pair in alist whose car is obj,
// where obj is compared using eq?
func builtinAssq(args []*Value, env *Environment) (*Value, error) {
	if len(args) != 2 {
		return nil, fmt.Errorf("assq expects exactly 2 arguments")
	}
	return assoc(args[0], args[1], builtinEqp, env)
}

// builtinAssv implements the assv procedure
// (assv obj alist) returns the first pair in alist whose car is obj,
// where obj is compared using eqv?
func builtinAssv(args []*Value, env *Environment) (*Value, error) {
	if len(args) != 2 {
		return nil, fmt.Errorf("assv expects exactly 2 arguments")
	}
	return assoc(args[0], args[1], builtinEqvp, env)
}

// builtinAssoc implements the assoc procedure
// (assoc obj alist) returns the first pair in alist whose car is obj,
// where obj is compared using equal?
func builtinAssoc(args []*Value, env *Environment) (*Value, error) {
	if len(args) != 2 {
		return nil, fmt.Errorf("assoc expects exactly 2 arguments")
	}
	return assoc(args[0], args[1], builtinEqualp, env)
}

// Helper function for assq, assv, assoc
func assoc(obj, alist *Value, compare func([]*Value, *Environment) (*Value, error), env *Environment) (*Value, error) {
	// Check if second argument is a list
	if alist.Type != TypeList && alist.Type != TypeNil && alist.Type != TypePair {
		return nil, fmt.Errorf("second argument must be an association list")
	}
	
	// Empty list returns #f
	if alist.Type == TypeNil || (alist.Type == TypeList && len(alist.List) == 0) {
		return NewBoolean(false), nil
	}
	
	// Handle TypeList
	if alist.Type == TypeList {
		for _, elem := range alist.List {
			// Check if element is a pair
			if elem.Type != TypePair && !(elem.Type == TypeList && len(elem.List) > 0) {
				return nil, fmt.Errorf("association list contains non-pair element")
			}
			
			// Get the car of the element
			var car *Value
			if elem.Type == TypePair {
				car = elem.Car
			} else {
				// It's a TypeList
				car = elem.List[0]
			}
			
			// Compare with obj
			result, err := compare([]*Value{obj, car}, env)
			if err != nil {
				return nil, err
			}
			if result.Boolean {
				return elem, nil
			}
		}
		return NewBoolean(false), nil
	}
	
	// Handle TypePair (and potentially dotted lists)
	current := alist
	seen := make(map[*Value]bool)
	
	for {
		// Check for circular lists
		if seen[current] {
			return nil, fmt.Errorf("circular association list detected")
		}
		seen[current] = true
		
		// Check if current element is a pair
		if current.Type != TypePair {
			return nil, fmt.Errorf("association list contains non-pair element")
		}
		
		// Get the car of the element
		result, err := compare([]*Value{obj, current.Car}, env)
		if err != nil {
			return nil, err
		}
		if result.Boolean {
			return current, nil
		}
		
		// Move to next element
		current = current.Cdr
		
		// Check if we've reached the end
		if current.Type == TypeNil || (current.Type == TypeList && len(current.List) == 0) {
			return NewBoolean(false), nil
		}
		if current.Type != TypePair && current.Type != TypeList {
			// Dotted list with non-list at the end
			return nil, fmt.Errorf("association list contains non-pair element")
		}
		if current.Type == TypeList {
			// Handle mixed TypeList at the end
			for _, elem := range current.List {
				// Check if element is a pair
				if elem.Type != TypePair && !(elem.Type == TypeList && len(elem.List) > 0) {
					return nil, fmt.Errorf("association list contains non-pair element")
				}
				
				// Get the car of the element
				var car *Value
				if elem.Type == TypePair {
					car = elem.Car
				} else {
					// It's a TypeList
					car = elem.List[0]
				}
				
				// Compare with obj
				result, err := compare([]*Value{obj, car}, env)
				if err != nil {
					return nil, err
				}
				if result.Boolean {
					return elem, nil
				}
			}
			return NewBoolean(false), nil
		}
	}
}

// Built-in list functions

func builtinCons(args []*Value, env *Environment) (*Value, error) {
	if len(args) != 2 {
		return nil, fmt.Errorf("cons expects exactly 2 arguments")
	}

	car := args[0]
	cdr := args[1]

	// Always create a pair - the String() method will handle proper vs dotted display
	return NewPair(car, cdr), nil
}

func builtinCar(args []*Value, env *Environment) (*Value, error) {
	if len(args) != 1 {
		return nil, fmt.Errorf("car expects exactly 1 argument")
	}

	arg := args[0]
	switch arg.Type {
	case TypePair:
		return arg.Car, nil
	case TypeList:
		if len(arg.List) == 0 {
			return nil, fmt.Errorf("car of empty list")
		}
		return arg.List[0], nil
	default:
		return nil, fmt.Errorf("car expects a pair or list")
	}
}

func builtinCdr(args []*Value, env *Environment) (*Value, error) {
	if len(args) != 1 {
		return nil, fmt.Errorf("cdr expects exactly 1 argument")
	}

	arg := args[0]
	switch arg.Type {
	case TypePair:
		return arg.Cdr, nil
	case TypeList:
		if len(arg.List) == 0 {
			return nil, fmt.Errorf("cdr of empty list")
		}
		if len(arg.List) == 1 {
			return NewNil(), nil
		}
		return NewList(arg.List[1:]), nil
	default:
		return nil, fmt.Errorf("cdr expects a pair or list")
	}
}

func builtinList(args []*Value, env *Environment) (*Value, error) {
	return NewList(args), nil
}

func builtinNullp(args []*Value, env *Environment) (*Value, error) {
	if len(args) != 1 {
		return nil, fmt.Errorf("null? expects exactly 1 argument")
	}

	return NewBoolean(args[0].Type == TypeNil || (args[0].Type == TypeList && len(args[0].List) == 0)), nil
}

// builtinAbs implements the abs procedure
// Returns the absolute value of a number
func builtinAbs(args []*Value, env *Environment) (*Value, error) {
	if len(args) != 1 {
		return nil, fmt.Errorf("abs expects exactly 1 argument")
	}

	arg := args[0]
	if !arg.IsNumber() {
		return nil, fmt.Errorf("abs expects a number")
	}

	// Calculate absolute value based on number type
	switch arg.Type {
	case TypeInteger:
		// For integers, use big.Int's Abs method
		result := new(big.Int).Abs(arg.Integer)
		return NewIntegerBig(result), nil
	case TypeRational:
		// For rationals, use big.Rat's Abs method
		result := new(big.Rat).Abs(arg.Rational)
		return NewRationalBig(result), nil
	case TypeReal:
		// For reals, use math.Abs function
		result := math.Abs(arg.Real)
		return NewReal(result, arg.Exact), nil
	default:
		// This shouldn't happen since we checked IsNumber()
		return nil, fmt.Errorf("abs: internal error")
	}
}

// builtinEqp implements the eq? predicate
func builtinEqp(args []*Value, env *Environment) (*Value, error) {
	if len(args) != 2 {
		return nil, fmt.Errorf("eq? expects exactly 2 arguments")
	}

	// Special case: empty list comparison (should always be #t)
	isFirstEmptyList := (args[0].Type == TypeNil) || (args[0].Type == TypeList && len(args[0].List) == 0)
	isSecondEmptyList := (args[1].Type == TypeNil) || (args[1].Type == TypeList && len(args[1].List) == 0)
	if isFirstEmptyList && isSecondEmptyList {
		return NewBoolean(true), nil
	}

	// For numbers, booleans, and symbols, use value equality
	if (args[0].IsNumber() && args[1].IsNumber()) ||
		(args[0].Type == TypeBoolean && args[1].Type == TypeBoolean) ||
		(args[0].Type == TypeSymbol && args[1].Type == TypeSymbol) {
		return NewBoolean(args[0].IsEqual(args[1])), nil
	}

	// For other types, check for identity (same object)
	return NewBoolean(args[0] == args[1]), nil
}

// builtinEqvp implements the eqv? predicate
// For most values, eqv? behaves like eq? (identity comparison)
// For numbers and characters, it compares their values
func builtinEqvp(args []*Value, env *Environment) (*Value, error) {
	if len(args) != 2 {
		return nil, fmt.Errorf("eqv? expects exactly 2 arguments")
	}

	// Special handling for empty list comparison
	// According to R4RS, (eqv? '() '()) should return #t
	// Handle two possible representations of empty list: TypeNil and empty TypeList
	isFirstEmptyList := (args[0].Type == TypeNil) || (args[0].Type == TypeList && len(args[0].List) == 0)
	isSecondEmptyList := (args[1].Type == TypeNil) || (args[1].Type == TypeList && len(args[1].List) == 0)
	if isFirstEmptyList && isSecondEmptyList {
		return NewBoolean(true), nil
	}

	// For numbers, booleans, symbols, strings, and nil, use value equality
	if (args[0].IsNumber() && args[1].IsNumber()) ||
		(args[0].Type == TypeBoolean && args[1].Type == TypeBoolean) ||
		(args[0].Type == TypeSymbol && args[1].Type == TypeSymbol) ||
		(args[0].Type == TypeString && args[1].Type == TypeString) ||
		(args[0].Type == TypeNil && args[1].Type == TypeNil) {
		return NewBoolean(args[0].IsEqual(args[1])), nil
	}

	// For other types (functions, builtins, non-empty lists), use identity comparison
	return NewBoolean(args[0] == args[1]), nil
}

func builtinEqualp(args []*Value, env *Environment) (*Value, error) {
	if len(args) != 2 {
		return nil, fmt.Errorf("equal? expects exactly 2 arguments")
	}

	return NewBoolean(args[0].IsEqual(args[1])), nil
}

// builtinPairp implements the pair? predicate
// Returns #t if the argument is a pair (non-empty list), #f otherwise
// In Scheme, a pair is any list with at least one element
func builtinPairp(args []*Value, env *Environment) (*Value, error) {
	if len(args) != 1 {
		return nil, fmt.Errorf("pair? expects exactly 1 argument")
	}

	arg := args[0]

	// A pair is either a TypePair or a non-empty TypeList
	if arg.Type == TypePair {
		return NewBoolean(true), nil
	}
	if arg.Type == TypeList && len(arg.List) > 0 {
		return NewBoolean(true), nil
	}

	return NewBoolean(false), nil
}

// Arithmetic helper functions

// addNumbers adds two numbers of any type
func addNumbers(a, b *Value) (*Value, error) {
	// If either is complex, do complex arithmetic
	if a.Type == TypeComplex || b.Type == TypeComplex {
		aComplex := a.ToComplex()
		bComplex := b.ToComplex()
		result := aComplex + bComplex
		// Result is exact if both operands are exact
		exact := a.Exact && b.Exact
		return NewComplex(result, exact), nil
	}

	// If both are integers, return integer
	if a.Type == TypeInteger && b.Type == TypeInteger {
		result := new(big.Int).Add(a.Integer, b.Integer)
		return NewIntegerBig(result), nil
	}

	// If both are exact rationals/integers, return rational
	if (a.Type == TypeInteger || a.Type == TypeRational) && (b.Type == TypeInteger || b.Type == TypeRational) && a.Exact && b.Exact {
		aRat := a.ToRational()
		bRat := b.ToRational()
		result := new(big.Rat).Add(aRat, bRat)
		return NewRationalBig(result), nil
	}

	// Otherwise, convert to float and return inexact real
	aFloat := a.ToFloat()
	bFloat := b.ToFloat()
	return NewInexactReal(aFloat + bFloat), nil
}

// subtractNumbers subtracts b from a
func subtractNumbers(a, b *Value) (*Value, error) {
	// If either is complex, do complex arithmetic
	if a.Type == TypeComplex || b.Type == TypeComplex {
		aComplex := a.ToComplex()
		bComplex := b.ToComplex()
		result := aComplex - bComplex
		// Result is exact if both operands are exact
		exact := a.Exact && b.Exact
		return NewComplex(result, exact), nil
	}

	// If both are integers, return integer
	if a.Type == TypeInteger && b.Type == TypeInteger {
		result := new(big.Int).Sub(a.Integer, b.Integer)
		return NewIntegerBig(result), nil
	}

	// If both are exact rationals/integers, return rational
	if (a.Type == TypeInteger || a.Type == TypeRational) && (b.Type == TypeInteger || b.Type == TypeRational) && a.Exact && b.Exact {
		aRat := a.ToRational()
		bRat := b.ToRational()
		result := new(big.Rat).Sub(aRat, bRat)
		return NewRationalBig(result), nil
	}

	// Otherwise, convert to float and return inexact real
	aFloat := a.ToFloat()
	bFloat := b.ToFloat()
	return NewInexactReal(aFloat - bFloat), nil
}

// multiplyNumbers multiplies two numbers
// multiplyNumbers multiplies two numbers of any type
func multiplyNumbers(a, b *Value) (*Value, error) {
	// If either is complex, do complex arithmetic
	if a.Type == TypeComplex || b.Type == TypeComplex {
		aComplex := a.ToComplex()
		bComplex := b.ToComplex()
		result := aComplex * bComplex
		// Result is exact if both operands are exact
		exact := a.Exact && b.Exact
		return NewComplex(result, exact), nil
	}

	// If both are integers, return integer
	if a.Type == TypeInteger && b.Type == TypeInteger {
		result := new(big.Int).Mul(a.Integer, b.Integer)
		return NewIntegerBig(result), nil
	}

	// If both are exact rationals/integers, return rational
	if (a.Type == TypeInteger || a.Type == TypeRational) && (b.Type == TypeInteger || b.Type == TypeRational) && a.Exact && b.Exact {
		aRat := a.ToRational()
		bRat := b.ToRational()
		result := new(big.Rat).Mul(aRat, bRat)
		return NewRationalBig(result), nil
	}

	// Otherwise, convert to float and return inexact real
	aFloat := a.ToFloat()
	bFloat := b.ToFloat()
	return NewInexactReal(aFloat * bFloat), nil
}

// divideNumbers divides a by b
func divideNumbers(a, b *Value) (*Value, error) {
	// Check for division by zero
	if isZero(b) {
		return nil, fmt.Errorf("division by zero")
	}

	// If either is complex, do complex arithmetic
	if a.Type == TypeComplex || b.Type == TypeComplex {
		aComplex := a.ToComplex()
		bComplex := b.ToComplex()
		// Check for division by zero in complex case
		if bComplex == 0 {
			return nil, fmt.Errorf("division by zero")
		}
		result := aComplex / bComplex
		// Result is exact if both operands are exact
		exact := a.Exact && b.Exact
		return NewComplex(result, exact), nil
	}

	// If both are exact rationals/integers, return rational
	if (a.Type == TypeInteger || a.Type == TypeRational) && (b.Type == TypeInteger || b.Type == TypeRational) && a.Exact && b.Exact {
		aRat := a.ToRational()
		bRat := b.ToRational()
		result := new(big.Rat).Quo(aRat, bRat)
		return NewRationalBig(result), nil
	}

	// Otherwise, convert to float and return inexact real
	aFloat := a.ToFloat()
	bFloat := b.ToFloat()
	return NewInexactReal(aFloat / bFloat), nil
}

// negateNumber returns the negation of a number
func negateNumber(a *Value) *Value {
	switch a.Type {
	case TypeInteger:
		result := new(big.Int).Neg(a.Integer)
		return NewIntegerBig(result)
	case TypeRational:
		result := new(big.Rat).Neg(a.Rational)
		return NewRationalBig(result)
	case TypeReal:
		return NewReal(-a.Real, a.Exact)
	case TypeComplex:
		return NewComplex(-a.Complex, a.Exact)
	default:
		return NewInteger(0) // Shouldn't happen
	}
}

// reciprocalNumber returns 1/a
func reciprocalNumber(a *Value) (*Value, error) {
	if isZero(a) {
		return nil, fmt.Errorf("division by zero")
	}

	switch a.Type {
	case TypeInteger:
		// 1/n becomes rational
		if a.Integer.Cmp(big.NewInt(1)) == 0 {
			return NewInteger(1), nil
		} else if a.Integer.Cmp(big.NewInt(-1)) == 0 {
			return NewInteger(-1), nil
		}
		result := new(big.Rat).SetFrac(big.NewInt(1), a.Integer)
		return NewRationalBig(result), nil
	case TypeRational:
		// Flip numerator and denominator
		result := new(big.Rat).Inv(a.Rational)
		return NewRationalBig(result), nil
	case TypeReal:
		return NewReal(1.0/a.Real, a.Exact), nil
	case TypeComplex:
		// 1/z = conjugate(z) / |z|^2
		if a.Complex == 0 {
			return nil, fmt.Errorf("division by zero")
		}
		result := complex(1, 0) / a.Complex
		return NewComplex(result, a.Exact), nil
	default:
		return nil, fmt.Errorf("reciprocal of non-number")
	}
}

// compareNumbers compares two numbers, returning -1, 0, or 1
func compareNumbers(a, b *Value) (int, error) {
	// If both are integers
	if a.Type == TypeInteger && b.Type == TypeInteger {
		return a.Integer.Cmp(b.Integer), nil
	}

	// If both are exact, use rational comparison
	if a.Exact && b.Exact {
		aRat := a.ToRational()
		bRat := b.ToRational()
		if aRat != nil && bRat != nil {
			return aRat.Cmp(bRat), nil
		}
	}

	// Use floating point comparison
	aFloat := a.ToFloat()
	bFloat := b.ToFloat()
	if aFloat < bFloat {
		return -1, nil
	} else if aFloat > bFloat {
		return 1, nil
	}
	return 0, nil
}

// isZero checks if a number is zero
func isZero(a *Value) bool {
	switch a.Type {
	case TypeInteger:
		return a.Integer.Sign() == 0
	case TypeRational:
		return a.Rational.Sign() == 0
	case TypeReal:
		return a.Real == 0.0
	case TypeComplex:
		return a.Complex == 0
	default:
		return false
	}
}

// builtinLoad implements the load procedure
// (load filename) reads and evaluates a Scheme file
func builtinLoad(args []*Value, env *Environment) (*Value, error) {
	if len(args) != 1 {
		return nil, fmt.Errorf("load expects exactly 1 argument")
	}

	if args[0].Type != TypeString {
		return nil, fmt.Errorf("load expects a string filename")
	}

	filename := args[0].StringVal

	// Read the file content
	content, err := os.ReadFile(filename)
	if err != nil {
		return nil, fmt.Errorf("cannot read file %s: %v", filename, err)
	}

	// Convert content to string
	input := string(content)

	// Skip #lang line if present
	lines := strings.Split(input, "\n")
	if len(lines) > 0 && strings.HasPrefix(strings.TrimSpace(lines[0]), "#lang") {
		// Skip the first line and rejoin
		input = strings.Join(lines[1:], "\n")
	}

	// Parse and evaluate the content
	// We'll evaluate each top-level expression in the file
	tokens, err := Tokenize(input)
	if err != nil {
		return nil, fmt.Errorf("error tokenizing file %s: %v", filename, err)
	}

	parser := NewParser(tokens)
	var lastResult *Value = NewNil()

	// Parse and evaluate all expressions in the file
	for parser.pos < len(parser.tokens)-1 { // -1 to account for EOF token
		if parser.current().Type == TokenEOF {
			break
		}

		// Get current position for error reporting before parsing
		currentToken := parser.current()
		currentLine := currentToken.Line
		currentCol := currentToken.Col

		expr, err := parser.parseExpression()
		if err != nil {
			return nil, fmt.Errorf("error parsing file %s at line %d, column %d: %v", filename, currentLine, currentCol, err)
		}

		// Evaluate the expression with line info
		result, err := EvalWithTCO(expr, env)
		if err != nil {
			return nil, fmt.Errorf("error evaluating file %s at line %d, column %d: %v", filename, currentLine, currentCol, err)
		}

		lastResult = result
	}

	// Return the result of the last expression
	return lastResult, nil
}

func builtinError(args []*Value, env *Environment) (*Value, error) {
	if len(args) != 1 {
		return nil, fmt.Errorf("error expects exactly 1 argument")
	}

	if args[0].Type != TypeString {
		return nil, fmt.Errorf("error expects a string argument")
	}

	return nil, fmt.Errorf("%s", args[0].StringVal)
}

func builtinNumberp(args []*Value, env *Environment) (*Value, error) {
	if len(args) != 1 {
		return nil, fmt.Errorf("number? expects exactly 1 argument")
	}

	return NewBoolean(args[0].IsNumber()), nil
}

func builtinExactNonnegativeIntegerp(args []*Value, env *Environment) (*Value, error) {
	if len(args) != 1 {
		return nil, fmt.Errorf("exact-nonnegative-integer? expects exactly 1 argument")
	}

	if !args[0].IsNumber() {
		return NewBoolean(false), nil
	}

	// Check if it's an exact integer and non-negative
	if args[0].Type == TypeInteger {
		return NewBoolean(args[0].Integer.Sign() >= 0), nil
	}

	// For other number types, check if they can be converted to non-negative integers
	if args[0].Type == TypeRational {
		// Check if it's a whole number (denominator is 1) and non-negative
		if args[0].Rational.Denom().Cmp(big.NewInt(1)) == 0 {
			return NewBoolean(args[0].Rational.Num().Sign() >= 0), nil
		}
		return NewBoolean(false), nil
	}

	// Real numbers are not exact integers
	return NewBoolean(false), nil
}

// builtinRemainder implements the remainder operation (a mod b)
func builtinRemainder(args []*Value, env *Environment) (*Value, error) {
	if len(args) != 2 {
		return nil, fmt.Errorf("remainder expects exactly 2 arguments")
	}

	if !args[0].IsNumber() || !args[1].IsNumber() {
		return nil, fmt.Errorf("remainder expects numbers")
	}

	a := args[0]
	b := args[1]

	// Check for division by zero
	if isZero(b) {
		return nil, fmt.Errorf("remainder by zero")
	}

	// Handle different number types
	switch {
	case a.Type == TypeInteger && b.Type == TypeInteger:
		// Integer remainder using big.Int
		result := new(big.Int).Rem(a.Integer, b.Integer)
		return NewIntegerBig(result), nil

	default:
		// For other types, convert to float and use floating-point remainder
		aFloat := a.ToFloat()
		bFloat := b.ToFloat()

		// Calculate remainder: a - b * floor(a/b)
		quotient := aFloat / bFloat
		floorQuotient := float64(int64(quotient))
		if quotient < 0 && quotient != floorQuotient {
			floorQuotient -= 1
		}
		remainder := aFloat - bFloat*floorQuotient

		return NewInexactReal(remainder), nil
	}
}

// builtinModulo implements the modulo operation
// In Scheme, modulo and remainder can have different behavior for negative numbers
// This implements the standard modulo operation
func builtinModulo(args []*Value, env *Environment) (*Value, error) {
	if len(args) != 2 {
		return nil, fmt.Errorf("modulo expects exactly 2 arguments")
	}

	if !args[0].IsNumber() || !args[1].IsNumber() {
		return nil, fmt.Errorf("modulo expects numbers")
	}

	a := args[0]
	b := args[1]

	// Check for division by zero
	if isZero(b) {
		return nil, fmt.Errorf("modulo by zero")
	}

	// Handle different number types
	switch {
	case a.Type == TypeInteger && b.Type == TypeInteger:
		// For integer modulo, we need to implement Scheme semantics
		// Go's big.Int.Mod gives remainder with same sign as dividend
		// Scheme modulo should give result with same sign as divisor
		remainder := new(big.Int).Rem(a.Integer, b.Integer)

		// If signs differ and remainder is non-zero, adjust the result
		if remainder.Sign() != 0 {
			bSign := b.Integer.Sign()
			rSign := remainder.Sign()

			// If remainder and divisor have different signs, adjust
			if (rSign > 0 && bSign < 0) || (rSign < 0 && bSign > 0) {
				remainder.Add(remainder, b.Integer)
			}
		}

		return NewIntegerBig(remainder), nil

	default:
		// For other types, implement proper modulo behavior using floor division
		aFloat := a.ToFloat()
		bFloat := b.ToFloat()

		// Calculate true modulo using floor division formula: a - b * floor(a/b)
		remainder := math.Mod(aFloat, bFloat)

		// Go's math.Mod returns remainder with same sign as dividend
		// For modulo, we want result with same sign as divisor
		if (remainder > 0 && bFloat < 0) || (remainder < 0 && bFloat > 0) {
			remainder += bFloat
		}

		return NewInexactReal(remainder), nil
	}
}

// builtinNegativep implements the negative? procedure
// (negative? x) returns #t if x is a negative number, #f otherwise
func builtinNegativep(args []*Value, env *Environment) (*Value, error) {
	if len(args) != 1 {
		return nil, fmt.Errorf("negative? expects exactly 1 argument")
	}

	if !args[0].IsNumber() {
		return NewBoolean(false), nil
	}

	x := args[0]
	switch x.Type {
	case TypeInteger:
		return NewBoolean(x.Integer.Sign() < 0), nil
	case TypeRational:
		return NewBoolean(x.Rational.Sign() < 0), nil
	case TypeReal:
		return NewBoolean(x.Real < 0.0), nil
	default:
		return NewBoolean(false), nil
	}
}

// builtinPositivep implements the positive? procedure
// (positive? x) returns #t if x is a positive number, #f otherwise
func builtinPositivep(args []*Value, env *Environment) (*Value, error) {
	if len(args) != 1 {
		return nil, fmt.Errorf("positive? expects exactly 1 argument")
	}

	if !args[0].IsNumber() {
		return NewBoolean(false), nil
	}

	x := args[0]
	switch x.Type {
	case TypeInteger:
		return NewBoolean(x.Integer.Sign() > 0), nil
	case TypeRational:
		return NewBoolean(x.Rational.Sign() > 0), nil
	case TypeReal:
		return NewBoolean(x.Real > 0.0), nil
	default:
		return NewBoolean(false), nil
	}
}

// builtinZerop implements the zero? procedure
// (zero? x) returns #t if x is zero, #f otherwise
func builtinZerop(args []*Value, env *Environment) (*Value, error) {
	if len(args) != 1 {
		return nil, fmt.Errorf("zero? expects exactly 1 argument")
	}

	if !args[0].IsNumber() {
		return NewBoolean(false), nil
	}

	return NewBoolean(isZero(args[0])), nil
}

// builtinFloor implements the floor procedure
// (floor x) returns the greatest integer less than or equal to x
func builtinFloor(args []*Value, env *Environment) (*Value, error) {
	if len(args) != 1 {
		return nil, fmt.Errorf("floor expects exactly 1 argument")
	}

	if !args[0].IsNumber() {
		return nil, fmt.Errorf("floor expects a number argument")
	}

	x := args[0]

	// For exact integers, floor is the integer itself
	if x.Type == TypeInteger && x.Exact {
		return NewIntegerBig(new(big.Int).Set(x.Integer)), nil
	}

	// Convert to float for floor operation
	xFloat := x.ToFloat()
	resultFloat := math.Floor(xFloat)

	// If the original number was exact, try to preserve exactness if possible
	if x.Exact {
		// Check if the result is an integer
		if resultFloat == float64(int64(resultFloat)) {
			return NewIntegerBig(big.NewInt(int64(resultFloat))), nil
		}
		// Otherwise, return as an exact real
		return NewReal(resultFloat, true), nil
	}

	// For inexact numbers, return as inexact real
	return NewInexactReal(resultFloat), nil
}

// builtinCeiling implements the ceiling procedure
// (ceiling x) returns the least integer greater than or equal to x
func builtinCeiling(args []*Value, env *Environment) (*Value, error) {
	if len(args) != 1 {
		return nil, fmt.Errorf("ceiling expects exactly 1 argument")
	}

	if !args[0].IsNumber() {
		return nil, fmt.Errorf("ceiling expects a number argument")
	}

	x := args[0]

	// For exact integers, ceiling is the integer itself
	if x.Type == TypeInteger && x.Exact {
		return NewIntegerBig(new(big.Int).Set(x.Integer)), nil
	}

	// Convert to float for ceiling operation
	xFloat := x.ToFloat()
	resultFloat := math.Ceil(xFloat)

	// If the original number was exact, try to preserve exactness if possible
	if x.Exact {
		// Check if the result is an integer
		if resultFloat == float64(int64(resultFloat)) {
			return NewIntegerBig(big.NewInt(int64(resultFloat))), nil
		}
		// Otherwise, return as an exact real
		return NewReal(resultFloat, true), nil
	}

	// For inexact numbers, return as inexact real
	return NewInexactReal(resultFloat), nil
}

// builtinTruncate implements the truncate procedure
// (truncate x) returns the integer closest to x whose absolute value is not larger than the absolute value of x
func builtinTruncate(args []*Value, env *Environment) (*Value, error) {
	if len(args) != 1 {
		return nil, fmt.Errorf("truncate expects exactly 1 argument")
	}

	if !args[0].IsNumber() {
		return nil, fmt.Errorf("truncate expects a number argument")
	}

	x := args[0]

	// For exact integers, truncate is the integer itself
	if x.Type == TypeInteger && x.Exact {
		return NewIntegerBig(new(big.Int).Set(x.Integer)), nil
	}

	// Convert to float for truncate operation
	xFloat := x.ToFloat()
	resultFloat := math.Trunc(xFloat)

	// If the original number was exact, try to preserve exactness if possible
	if x.Exact {
		// Check if the result is an integer
		if resultFloat == float64(int64(resultFloat)) {
			return NewIntegerBig(big.NewInt(int64(resultFloat))), nil
		}
		// Otherwise, return as an exact real
		return NewReal(resultFloat, true), nil
	}

	// For inexact numbers, return as inexact real
	return NewInexactReal(resultFloat), nil
}

// builtinRound implements the round procedure
// (round x) returns the closest integer to x, with halfway cases rounded to even
func builtinRound(args []*Value, env *Environment) (*Value, error) {
	if len(args) != 1 {
		return nil, fmt.Errorf("round expects exactly 1 argument")
	}

	if !args[0].IsNumber() {
		return nil, fmt.Errorf("round expects a number argument")
	}

	x := args[0]

	// For exact integers, round is the integer itself
	if x.Type == TypeInteger && x.Exact {
		return NewIntegerBig(new(big.Int).Set(x.Integer)), nil
	}

	// Convert to float for round operation
	xFloat := x.ToFloat()
	resultFloat := math.Round(xFloat)

	// If the original number was exact, try to preserve exactness if possible
	if x.Exact {
		// Check if the result is an integer
		if resultFloat == float64(int64(resultFloat)) {
			return NewIntegerBig(big.NewInt(int64(resultFloat))), nil
		}
		// Otherwise, return as an exact real
		return NewReal(resultFloat, true), nil
	}

	// For inexact numbers, return as inexact real
	return NewInexactReal(resultFloat), nil
}

// builtinMax implements the max procedure
// (max x y ...) returns the maximum of the arguments
func builtinMax(args []*Value, env *Environment) (*Value, error) {
	if len(args) < 1 {
		return nil, fmt.Errorf("max expects at least 1 argument")
	}

	// Check all arguments are numbers
	for _, arg := range args {
		if !arg.IsNumber() {
			return nil, fmt.Errorf("max expects number arguments")
		}
	}

	// Start with the first argument as the maximum
	max := args[0]

	// Compare with other arguments
	for i := 1; i < len(args); i++ {
		result, err := compareNumbers(max, args[i])
		if err != nil {
			return nil, err
		}
		// If current max is less than args[i], update max
		if result < 0 {
			max = args[i]
		}
	}

	return max, nil
}

// builtinMin implements the min procedure
// (min x y ...) returns the minimum of the arguments
func builtinMin(args []*Value, env *Environment) (*Value, error) {
	if len(args) < 1 {
		return nil, fmt.Errorf("min expects at least 1 argument")
	}

	// Check all arguments are numbers
	for _, arg := range args {
		if !arg.IsNumber() {
			return nil, fmt.Errorf("min expects number arguments")
		}
	}

	// Start with the first argument as the minimum
	min := args[0]

	// Compare with other arguments
	for i := 1; i < len(args); i++ {
		result, err := compareNumbers(min, args[i])
		if err != nil {
			return nil, err
		}
		// If current min is greater than args[i], update min
		if result > 0 {
			min = args[i]
		}
	}

	return min, nil
}

// builtinQuotient implements the quotient procedure
// (quotient n m) returns the integer quotient of n divided by m
func builtinQuotient(args []*Value, env *Environment) (*Value, error) {
	if len(args) != 2 {
		return nil, fmt.Errorf("quotient expects exactly 2 arguments")
	}

	if !args[0].IsNumber() || !args[1].IsNumber() {
		return nil, fmt.Errorf("quotient expects number arguments")
	}

	a := args[0]
	b := args[1]

	// Check for division by zero
	if isZero(b) {
		return nil, fmt.Errorf("division by zero in quotient")
	}

	// Handle different number types
	switch {
	case a.Type == TypeInteger && b.Type == TypeInteger:
		// For integers, use big.Int's Quo function
		result := new(big.Int).Quo(a.Integer, b.Integer)
		return NewIntegerBig(result), nil

	default:
		// For other types, convert to float and use integer division
		aFloat := a.ToFloat()
		bFloat := b.ToFloat()

		// Perform integer division
		// In Scheme, quotient rounds towards negative infinity
		var resultFloat float64
		if aFloat*bFloat >= 0 {
			// Same signs: truncate towards zero
			resultFloat = math.Trunc(aFloat / bFloat)
		} else {
			// Different signs: truncate towards negative infinity
			resultFloat = math.Floor(aFloat / bFloat)
		}

		// Try to preserve exactness if possible
		if a.Exact && b.Exact {
			// Check if the result is an integer
			if resultFloat == float64(int64(resultFloat)) {
				return NewIntegerBig(big.NewInt(int64(resultFloat))), nil
			}
			// Otherwise, return as an exact real
			return NewReal(resultFloat, true), nil
		}

		// For inexact numbers, return as inexact real
		return NewInexactReal(resultFloat), nil
	}
}

// builtinOddp implements the odd? procedure
// (odd? n) returns #t if n is an odd integer, #f otherwise
func builtinOddp(args []*Value, env *Environment) (*Value, error) {
	if len(args) != 1 {
		return nil, fmt.Errorf("odd? expects exactly 1 argument")
	}

	n := args[0]
	if !n.IsNumber() {
		return NewBoolean(false), nil
	}

	// Convert to integer if possible
	intVal := n.ToInteger()
	if intVal == nil {
		return NewBoolean(false), nil
	}

	// Check if the integer is odd
	// An integer is odd if it is not divisible by 2
	remainder := new(big.Int)
	remainder.Mod(intVal, big.NewInt(2))
	return NewBoolean(remainder.Sign() != 0), nil
}

// builtinEvenp implements the even? procedure
// (even? n) returns #t if n is an even integer, #f otherwise
func builtinEvenp(args []*Value, env *Environment) (*Value, error) {
	if len(args) != 1 {
		return nil, fmt.Errorf("even? expects exactly 1 argument")
	}

	n := args[0]
	if !n.IsNumber() {
		return NewBoolean(false), nil
	}

	// Convert to integer if possible
	intVal := n.ToInteger()
	if intVal == nil {
		return NewBoolean(false), nil
	}

	// Check if the integer is even
	// An integer is even if it is divisible by 2
	remainder := new(big.Int)
	remainder.Mod(intVal, big.NewInt(2))
	return NewBoolean(remainder.Sign() == 0), nil
}

// builtinGcd implements the gcd procedure
// (gcd n m ...) returns the greatest common divisor of the absolute values of the arguments
func builtinGcd(args []*Value, env *Environment) (*Value, error) {
	if len(args) < 1 {
		return nil, fmt.Errorf("gcd expects at least 1 argument")
	}

	// Check all arguments are numbers
	for _, arg := range args {
		if !arg.IsNumber() {
			return nil, fmt.Errorf("gcd expects number arguments")
		}
	}

	// Convert all arguments to integers
	integers := make([]*big.Int, 0, len(args))
	for _, arg := range args {
		intVal := arg.ToInteger()
		if intVal == nil {
			return nil, fmt.Errorf("gcd expects integer arguments")
		}
		// Take absolute value
		intVal.Abs(intVal)
		integers = append(integers, intVal)
	}

	// Calculate GCD of all integers
	result := new(big.Int).Set(integers[0])
	for i := 1; i < len(integers); i++ {
		result.GCD(nil, nil, result, integers[i])
	}

	// Handle the case where all arguments are zero
	if result.Sign() == 0 && len(integers) > 0 {
		// If all arguments are zero, return zero
		allZero := true
		for _, i := range integers {
			if i.Sign() != 0 {
				allZero = false
				break
			}
		}
		if allZero {
			return NewInteger(0), nil
		}
	}

	return NewIntegerBig(result), nil
}

// builtinLcm implements the lcm procedure
// (lcm n m ...) returns the least common multiple of the absolute values of the arguments
func builtinLcm(args []*Value, env *Environment) (*Value, error) {
	if len(args) < 1 {
		return nil, fmt.Errorf("lcm expects at least 1 argument")
	}

	// Check all arguments are numbers
	for _, arg := range args {
		if !arg.IsNumber() {
			return nil, fmt.Errorf("lcm expects number arguments")
		}
	}

	// Convert all arguments to integers
	integers := make([]*big.Int, 0, len(args))
	hasZero := false
	for _, arg := range args {
		intVal := arg.ToInteger()
		if intVal == nil {
			return nil, fmt.Errorf("lcm expects integer arguments")
		}
		// Check if any argument is zero
		if intVal.Sign() == 0 {
			hasZero = true
		}
		// Take absolute value
		intVal.Abs(intVal)
		integers = append(integers, intVal)
	}

	// If any argument is zero, the lcm is zero
	if hasZero {
		return NewInteger(0), nil
	}

	// Calculate LCM of all integers
	// LCM(a,b) = |a*b| / GCD(a,b)
	result := new(big.Int).Set(integers[0])
	for i := 1; i < len(integers); i++ {
		// Calculate LCM of current result and next integer
		gcd := new(big.Int).GCD(nil, nil, result, integers[i])
		// LCM = (result * integer[i]) / GCD
		result.Mul(result, integers[i])
		result.Div(result, gcd)
	}

	return NewIntegerBig(result), nil
}

// builtinSetCar implements the set-car! procedure
// (set-car! pair obj) modifies the car of pair to be obj
func builtinSetCar(args []*Value, env *Environment) (*Value, error) {
	if len(args) != 2 {
		return nil, fmt.Errorf("set-car! expects exactly 2 arguments")
	}

	pair := args[0]
	if pair.Type != TypePair && (pair.Type != TypeList || len(pair.List) == 0) {
		return nil, fmt.Errorf("set-car! expects a pair")
	}

	// Handle TypePair
	if pair.Type == TypePair {
		pair.Car = args[1]
	} else {
		// Handle TypeList (legacy support)
		pair.List[0] = args[1]
	}

	// According to R4RS, set-car! returns unspecified value
	// We'll return #t for simplicity
	return NewBoolean(true), nil
}

// builtinSetCdr implements the set-cdr! procedure
// (set-cdr! pair obj) modifies the cdr of pair to be obj
func builtinSetCdr(args []*Value, env *Environment) (*Value, error) {
	if len(args) != 2 {
		return nil, fmt.Errorf("set-cdr! expects exactly 2 arguments")
	}

	pair := args[0]
	if pair.Type != TypePair && (pair.Type != TypeList || len(pair.List) == 0) {
		return nil, fmt.Errorf("set-cdr! expects a pair")
	}

	newCdr := args[1]

	// Handle TypePair
	if pair.Type == TypePair {
		pair.Cdr = newCdr
	} else {
		// Handle TypeList (legacy support)
		// If the pair has only one element, set-cdr! should set it to the new cdr
		if len(pair.List) == 1 {
			// We need to create a new list to represent the new cdr
			// According to Scheme semantics, we'll create a new list with the car and new cdr
			pair.List = []*Value{pair.List[0], newCdr}
		} else {
			// For a proper list (with more than one element), replace everything after the first element
			// This is a simplified implementation
			if newCdr.Type == TypeList {
				// If the new cdr is a list, append its elements
				pair.List = append([]*Value{pair.List[0]}, newCdr.List...)
			} else if newCdr.Type == TypeNil {
				// If the new cdr is nil, keep only the car
				pair.List = []*Value{pair.List[0]}
			} else {
				// For an improper list (dotted pair)
				pair.List = []*Value{pair.List[0], newCdr}
			}
		}
	}

	// According to R4RS, set-cdr! returns unspecified value
	// We'll return #t for simplicity
	return NewBoolean(true), nil
}

// builtinAppend implements the append procedure
// (append list1 list2 ...) returns a new list formed by appending the lists
func builtinAppend(args []*Value, env *Environment) (*Value, error) {
	if len(args) == 0 {
		return NewNil(), nil
	}

	// Check all arguments are lists
	for i := 0; i < len(args)-1; i++ {
		arg := args[i]
		if arg.Type != TypeList && arg.Type != TypeNil {
			return nil, fmt.Errorf("append expects lists as arguments except possibly the last")
		}
	}

	// Create a new list to hold the result
	var result []*Value

	// Append all elements from all but the last argument
	for i := 0; i < len(args)-1; i++ {
		arg := args[i]
		if arg.Type == TypeList {
			result = append(result, arg.List...)
		}
		// Skip nil arguments (empty lists)
	}

	// Append the last argument
	lastArg := args[len(args)-1]
	if lastArg.Type == TypeList {
		result = append(result, lastArg.List...)
	} else {
		// For the last argument, we can append any value (to create improper lists)
		if len(result) == 0 {
			// If result is empty, just return the last argument
			return lastArg, nil
		} else {
			// Otherwise, create a new list with all elements and the last argument as cdr
			// This is a simplified implementation for improper lists
			return NewList(append(result, lastArg)), nil
		}
	}

	return NewList(result), nil
}

// builtinReverse implements the reverse procedure
// (reverse list) returns a new list with the elements in reverse order
func builtinReverse(args []*Value, env *Environment) (*Value, error) {
	if len(args) != 1 {
		return nil, fmt.Errorf("reverse expects exactly 1 argument")
	}

	arg := args[0]
	if arg.Type != TypeList && arg.Type != TypeNil {
		return nil, fmt.Errorf("reverse expects a list")
	}

	// Handle nil as empty list
	if arg.Type == TypeNil {
		return NewNil(), nil
	}

	// Create a new list with elements in reverse order
	length := len(arg.List)
	result := make([]*Value, length)

	for i := 0; i < length; i++ {
		result[length-1-i] = arg.List[i]
	}

	return NewList(result), nil
}

// builtinBooleanp implements the boolean? predicate
func builtinBooleanp(args []*Value, env *Environment) (*Value, error) {
	if len(args) != 1 {
		return nil, fmt.Errorf("boolean? expects exactly 1 argument")
	}

	return NewBoolean(args[0].Type == TypeBoolean), nil
}

// builtinSymbolp implements the symbol? predicate
func builtinSymbolp(args []*Value, env *Environment) (*Value, error) {
	if len(args) != 1 {
		return nil, fmt.Errorf("symbol? expects exactly 1 argument")
	}

	return NewBoolean(args[0].Type == TypeSymbol), nil
}

// builtinStringp implements the string? predicate
func builtinStringp(args []*Value, env *Environment) (*Value, error) {
	if len(args) != 1 {
		return nil, fmt.Errorf("string? expects exactly 1 argument")
	}

	return NewBoolean(args[0].Type == TypeString), nil
}

// String procedures (R4RS section 6.7)

// builtinMakeString implements make-string
// (make-string k) creates a string of length k filled with #\null
// (make-string k char) creates a string of length k filled with char
func builtinMakeString(args []*Value, env *Environment) (*Value, error) {
	if len(args) < 1 || len(args) > 2 {
		return nil, fmt.Errorf("make-string expects 1 or 2 arguments")
	}

	if !args[0].IsNumber() {
		return nil, fmt.Errorf("make-string: first argument must be a number")
	}

	length := int(args[0].ToFloat())
	if length < 0 {
		return nil, fmt.Errorf("make-string: length must be non-negative")
	}

	fillChar := '\x00' // null character by default
	if len(args) == 2 {
		if args[1].Type != TypeChar {
			return nil, fmt.Errorf("make-string: second argument must be a character")
		}
		fillChar = args[1].Char
	}

	// Create string of specified length filled with the character
	result := make([]rune, length)
	for i := 0; i < length; i++ {
		result[i] = fillChar
	}

	return NewString(string(result)), nil
}

// builtinString implements string
// (string char ...) creates a string from the given characters
func builtinString(args []*Value, env *Environment) (*Value, error) {
	result := make([]rune, len(args))
	for i, arg := range args {
		if arg.Type != TypeChar {
			return nil, fmt.Errorf("string: all arguments must be characters")
		}
		result[i] = arg.Char
	}

	return NewString(string(result)), nil
}

// builtinStringLength implements string-length
// (string-length string) returns the number of characters in string
func builtinStringLength(args []*Value, env *Environment) (*Value, error) {
	if len(args) != 1 {
		return nil, fmt.Errorf("string-length expects exactly 1 argument")
	}

	if args[0].Type != TypeString {
		return nil, fmt.Errorf("string-length expects a string")
	}

	// Use rune length to handle Unicode correctly
	length := len([]rune(args[0].StringVal))
	return NewInteger(int64(length)), nil
}

// builtinStringRef implements string-ref
// (string-ref string k) returns character k of string (0-indexed)
func builtinStringRef(args []*Value, env *Environment) (*Value, error) {
	if len(args) != 2 {
		return nil, fmt.Errorf("string-ref expects exactly 2 arguments")
	}

	if args[0].Type != TypeString {
		return nil, fmt.Errorf("string-ref: first argument must be a string")
	}

	if !args[1].IsNumber() {
		return nil, fmt.Errorf("string-ref: second argument must be a number")
	}

	str := []rune(args[0].StringVal)
	index := int(args[1].ToFloat())

	if index < 0 || index >= len(str) {
		return nil, fmt.Errorf("string-ref: index out of range")
	}

	return NewChar(str[index]), nil
}

// builtinStringSet implements string-set!
// (string-set! string k char) sets character k of string to char
func builtinStringSet(args []*Value, env *Environment) (*Value, error) {
	if len(args) != 3 {
		return nil, fmt.Errorf("string-set! expects exactly 3 arguments")
	}

	if args[0].Type != TypeString {
		return nil, fmt.Errorf("string-set!: first argument must be a string")
	}

	if !args[1].IsNumber() {
		return nil, fmt.Errorf("string-set!: second argument must be a number")
	}

	if args[2].Type != TypeChar {
		return nil, fmt.Errorf("string-set!: third argument must be a character")
	}

	str := []rune(args[0].StringVal)
	index := int(args[1].ToFloat())

	if index < 0 || index >= len(str) {
		return nil, fmt.Errorf("string-set!: index out of range")
	}

	// Modify the string by updating the character at the index
	str[index] = args[2].Char
	args[0].StringVal = string(str)

	// Return unspecified value
	return NewSymbol("#<unspecified>"), nil
}

// builtinStringEqual implements string=?
// (string=? string1 string2) returns #t if strings are equal
func builtinStringEqual(args []*Value, env *Environment) (*Value, error) {
	if len(args) != 2 {
		return nil, fmt.Errorf("string=? expects exactly 2 arguments")
	}

	if args[0].Type != TypeString || args[1].Type != TypeString {
		return nil, fmt.Errorf("string=? expects strings")
	}

	return NewBoolean(args[0].StringVal == args[1].StringVal), nil
}

// builtinStringCiEqual implements string-ci=?
// (string-ci=? string1 string2) returns #t if strings are equal ignoring case
func builtinStringCiEqual(args []*Value, env *Environment) (*Value, error) {
	if len(args) != 2 {
		return nil, fmt.Errorf("string-ci=? expects exactly 2 arguments")
	}

	if args[0].Type != TypeString || args[1].Type != TypeString {
		return nil, fmt.Errorf("string-ci=? expects strings")
	}

	return NewBoolean(strings.EqualFold(args[0].StringVal, args[1].StringVal)), nil
}

// builtinStringLess implements string<?
// (string<? string1 string2) returns #t if string1 is lexicographically less than string2
func builtinStringLess(args []*Value, env *Environment) (*Value, error) {
	if len(args) != 2 {
		return nil, fmt.Errorf("string<? expects exactly 2 arguments")
	}

	if args[0].Type != TypeString || args[1].Type != TypeString {
		return nil, fmt.Errorf("string<? expects strings")
	}

	return NewBoolean(args[0].StringVal < args[1].StringVal), nil
}

// builtinStringGreater implements string>?
// (string>? string1 string2) returns #t if string1 is lexicographically greater than string2
func builtinStringGreater(args []*Value, env *Environment) (*Value, error) {
	if len(args) != 2 {
		return nil, fmt.Errorf("string>? expects exactly 2 arguments")
	}

	if args[0].Type != TypeString || args[1].Type != TypeString {
		return nil, fmt.Errorf("string>? expects strings")
	}

	return NewBoolean(args[0].StringVal > args[1].StringVal), nil
}

// builtinStringLessEqual implements string<=?
// (string<=? string1 string2) returns #t if string1 is lexicographically less than or equal to string2
func builtinStringLessEqual(args []*Value, env *Environment) (*Value, error) {
	if len(args) != 2 {
		return nil, fmt.Errorf("string<=? expects exactly 2 arguments")
	}

	if args[0].Type != TypeString || args[1].Type != TypeString {
		return nil, fmt.Errorf("string<=? expects strings")
	}

	return NewBoolean(args[0].StringVal <= args[1].StringVal), nil
}

// builtinStringGreaterEqual implements string>=?
// (string>=? string1 string2) returns #t if string1 is lexicographically greater than or equal to string2
func builtinStringGreaterEqual(args []*Value, env *Environment) (*Value, error) {
	if len(args) != 2 {
		return nil, fmt.Errorf("string>=? expects exactly 2 arguments")
	}

	if args[0].Type != TypeString || args[1].Type != TypeString {
		return nil, fmt.Errorf("string>=? expects strings")
	}

	return NewBoolean(args[0].StringVal >= args[1].StringVal), nil
}

// builtinStringCiLess implements string-ci<?
// (string-ci<? string1 string2) returns #t if string1 is lexicographically less than string2 (case-insensitive)
func builtinStringCiLess(args []*Value, env *Environment) (*Value, error) {
	if len(args) != 2 {
		return nil, fmt.Errorf("string-ci<? expects exactly 2 arguments")
	}

	if args[0].Type != TypeString || args[1].Type != TypeString {
		return nil, fmt.Errorf("string-ci<? expects strings")
	}

	return NewBoolean(strings.ToLower(args[0].StringVal) < strings.ToLower(args[1].StringVal)), nil
}

// builtinStringCiGreater implements string-ci>?
// (string-ci>? string1 string2) returns #t if string1 is lexicographically greater than string2 (case-insensitive)
func builtinStringCiGreater(args []*Value, env *Environment) (*Value, error) {
	if len(args) != 2 {
		return nil, fmt.Errorf("string-ci>? expects exactly 2 arguments")
	}

	if args[0].Type != TypeString || args[1].Type != TypeString {
		return nil, fmt.Errorf("string-ci>? expects strings")
	}

	return NewBoolean(strings.ToLower(args[0].StringVal) > strings.ToLower(args[1].StringVal)), nil
}

// builtinStringCiLessEqual implements string-ci<=?
// (string-ci<=? string1 string2) returns #t if string1 is lexicographically less than or equal to string2 (case-insensitive)
func builtinStringCiLessEqual(args []*Value, env *Environment) (*Value, error) {
	if len(args) != 2 {
		return nil, fmt.Errorf("string-ci<=? expects exactly 2 arguments")
	}

	if args[0].Type != TypeString || args[1].Type != TypeString {
		return nil, fmt.Errorf("string-ci<=? expects strings")
	}

	return NewBoolean(strings.ToLower(args[0].StringVal) <= strings.ToLower(args[1].StringVal)), nil
}

// builtinStringCiGreaterEqual implements string-ci>=?
// (string-ci>=? string1 string2) returns #t if string1 is lexicographically greater than or equal to string2 (case-insensitive)
func builtinStringCiGreaterEqual(args []*Value, env *Environment) (*Value, error) {
	if len(args) != 2 {
		return nil, fmt.Errorf("string-ci>=? expects exactly 2 arguments")
	}

	if args[0].Type != TypeString || args[1].Type != TypeString {
		return nil, fmt.Errorf("string-ci>=? expects strings")
	}

	return NewBoolean(strings.ToLower(args[0].StringVal) >= strings.ToLower(args[1].StringVal)), nil
}

// builtinSubstring implements substring
// (substring string start end) returns a substring from start to end (exclusive)
func builtinSubstring(args []*Value, env *Environment) (*Value, error) {
	if len(args) != 3 {
		return nil, fmt.Errorf("substring expects exactly 3 arguments")
	}

	if args[0].Type != TypeString {
		return nil, fmt.Errorf("substring: first argument must be a string")
	}

	if !args[1].IsNumber() || !args[2].IsNumber() {
		return nil, fmt.Errorf("substring: start and end indices must be numbers")
	}

	str := []rune(args[0].StringVal)
	start := int(args[1].ToFloat())
	end := int(args[2].ToFloat())

	if start < 0 || end < 0 || start > len(str) || end > len(str) || start > end {
		return nil, fmt.Errorf("substring: invalid indices")
	}

	return NewString(string(str[start:end])), nil
}

// builtinStringAppend implements string-append
// (string-append string ...) concatenates all the strings
func builtinStringAppend(args []*Value, env *Environment) (*Value, error) {
	var result strings.Builder

	for _, arg := range args {
		if arg.Type != TypeString {
			return nil, fmt.Errorf("string-append: all arguments must be strings")
		}
		result.WriteString(arg.StringVal)
	}

	return NewString(result.String()), nil
}

// builtinStringToList implements string->list
// (string->list string) converts a string to a list of characters
func builtinStringToList(args []*Value, env *Environment) (*Value, error) {
	if len(args) != 1 {
		return nil, fmt.Errorf("string->list expects exactly 1 argument")
	}

	if args[0].Type != TypeString {
		return nil, fmt.Errorf("string->list expects a string")
	}

	str := []rune(args[0].StringVal)
	result := make([]*Value, len(str))

	for i, char := range str {
		result[i] = NewChar(char)
	}

	return NewList(result), nil
}

// builtinListToString implements list->string
// (list->string list) converts a list of characters to a string
func builtinListToString(args []*Value, env *Environment) (*Value, error) {
	if len(args) != 1 {
		return nil, fmt.Errorf("list->string expects exactly 1 argument")
	}

	if args[0].Type != TypeList {
		return nil, fmt.Errorf("list->string expects a list")
	}

	result := make([]rune, len(args[0].List))
	for i, val := range args[0].List {
		if val.Type != TypeChar {
			return nil, fmt.Errorf("list->string: all list elements must be characters")
		}
		result[i] = val.Char
	}

	return NewString(string(result)), nil
}

// builtinStringCopy implements string-copy
// (string-copy string) creates a copy of the string
func builtinStringCopy(args []*Value, env *Environment) (*Value, error) {
	if len(args) != 1 {
		return nil, fmt.Errorf("string-copy expects exactly 1 argument")
	}

	if args[0].Type != TypeString {
		return nil, fmt.Errorf("string-copy expects a string")
	}

	return NewString(args[0].StringVal), nil
}

// builtinStringFill implements string-fill!
// (string-fill! string char) fills the string with the character
func builtinStringFill(args []*Value, env *Environment) (*Value, error) {
	if len(args) != 2 {
		return nil, fmt.Errorf("string-fill! expects exactly 2 arguments")
	}

	if args[0].Type != TypeString {
		return nil, fmt.Errorf("string-fill!: first argument must be a string")
	}

	if args[1].Type != TypeChar {
		return nil, fmt.Errorf("string-fill!: second argument must be a character")
	}

	str := []rune(args[0].StringVal)
	fillChar := args[1].Char

	for i := range str {
		str[i] = fillChar
	}

	args[0].StringVal = string(str)

	// Return unspecified value
	return NewSymbol("#<unspecified>"), nil
}

// builtinExactp implements the exact? predicate
func builtinExactp(args []*Value, env *Environment) (*Value, error) {
	if len(args) != 1 {
		return nil, fmt.Errorf("exact? expects exactly 1 argument")
	}

	if !args[0].IsNumber() {
		return nil, fmt.Errorf("exact? expects a number")
	}

	return NewBoolean(args[0].Exact), nil
}

// builtinInexactp implements the inexact? predicate
func builtinInexactp(args []*Value, env *Environment) (*Value, error) {
	if len(args) != 1 {
		return nil, fmt.Errorf("inexact? expects exactly 1 argument")
	}

	if !args[0].IsNumber() {
		return nil, fmt.Errorf("inexact? expects a number")
	}

	return NewBoolean(!args[0].Exact), nil
}

// Character procedures (R4RS section 6.6)

// builtinCharp implements the char? predicate
func builtinCharp(args []*Value, env *Environment) (*Value, error) {
	if len(args) != 1 {
		return nil, fmt.Errorf("char? expects exactly 1 argument")
	}

	return NewBoolean(args[0].Type == TypeChar), nil
}

// builtinCharEqualp implements char=?
func builtinCharEqualp(args []*Value, env *Environment) (*Value, error) {
	if len(args) != 2 {
		return nil, fmt.Errorf("char=? expects exactly 2 arguments")
	}

	if args[0].Type != TypeChar || args[1].Type != TypeChar {
		return nil, fmt.Errorf("char=? expects characters")
	}

	return NewBoolean(args[0].Char == args[1].Char), nil
}

// builtinCharLessp implements char<?
func builtinCharLessp(args []*Value, env *Environment) (*Value, error) {
	if len(args) != 2 {
		return nil, fmt.Errorf("char<? expects exactly 2 arguments")
	}

	if args[0].Type != TypeChar || args[1].Type != TypeChar {
		return nil, fmt.Errorf("char<? expects characters")
	}

	return NewBoolean(args[0].Char < args[1].Char), nil
}

// builtinCharGreaterp implements char>?
func builtinCharGreaterp(args []*Value, env *Environment) (*Value, error) {
	if len(args) != 2 {
		return nil, fmt.Errorf("char>? expects exactly 2 arguments")
	}

	if args[0].Type != TypeChar || args[1].Type != TypeChar {
		return nil, fmt.Errorf("char>? expects characters")
	}

	return NewBoolean(args[0].Char > args[1].Char), nil
}

// builtinCharLessEqualp implements char<=?
func builtinCharLessEqualp(args []*Value, env *Environment) (*Value, error) {
	if len(args) != 2 {
		return nil, fmt.Errorf("char<=? expects exactly 2 arguments")
	}

	if args[0].Type != TypeChar || args[1].Type != TypeChar {
		return nil, fmt.Errorf("char<=? expects characters")
	}

	return NewBoolean(args[0].Char <= args[1].Char), nil
}

// builtinCharGreaterEqualp implements char>=?
func builtinCharGreaterEqualp(args []*Value, env *Environment) (*Value, error) {
	if len(args) != 2 {
		return nil, fmt.Errorf("char>=? expects exactly 2 arguments")
	}

	if args[0].Type != TypeChar || args[1].Type != TypeChar {
		return nil, fmt.Errorf("char>=? expects characters")
	}

	return NewBoolean(args[0].Char >= args[1].Char), nil
}

// builtinCharCiEqualp implements char-ci=? (case-insensitive equal)
func builtinCharCiEqualp(args []*Value, env *Environment) (*Value, error) {
	if len(args) != 2 {
		return nil, fmt.Errorf("char-ci=? expects exactly 2 arguments")
	}

	if args[0].Type != TypeChar || args[1].Type != TypeChar {
		return nil, fmt.Errorf("char-ci=? expects characters")
	}

	return NewBoolean(unicode.ToLower(args[0].Char) == unicode.ToLower(args[1].Char)), nil
}

// builtinCharCiLessp implements char-ci<? (case-insensitive less)
func builtinCharCiLessp(args []*Value, env *Environment) (*Value, error) {
	if len(args) != 2 {
		return nil, fmt.Errorf("char-ci<? expects exactly 2 arguments")
	}

	if args[0].Type != TypeChar || args[1].Type != TypeChar {
		return nil, fmt.Errorf("char-ci<? expects characters")
	}

	return NewBoolean(unicode.ToLower(args[0].Char) < unicode.ToLower(args[1].Char)), nil
}

// builtinCharCiGreaterp implements char-ci>? (case-insensitive greater)
func builtinCharCiGreaterp(args []*Value, env *Environment) (*Value, error) {
	if len(args) != 2 {
		return nil, fmt.Errorf("char-ci>? expects exactly 2 arguments")
	}

	if args[0].Type != TypeChar || args[1].Type != TypeChar {
		return nil, fmt.Errorf("char-ci>? expects characters")
	}

	return NewBoolean(unicode.ToLower(args[0].Char) > unicode.ToLower(args[1].Char)), nil
}

// builtinCharCiLessEqualp implements char-ci<=? (case-insensitive less or equal)
func builtinCharCiLessEqualp(args []*Value, env *Environment) (*Value, error) {
	if len(args) != 2 {
		return nil, fmt.Errorf("char-ci<=? expects exactly 2 arguments")
	}

	if args[0].Type != TypeChar || args[1].Type != TypeChar {
		return nil, fmt.Errorf("char-ci<=? expects characters")
	}

	return NewBoolean(unicode.ToLower(args[0].Char) <= unicode.ToLower(args[1].Char)), nil
}

// builtinCharCiGreaterEqualp implements char-ci>=? (case-insensitive greater or equal)
func builtinCharCiGreaterEqualp(args []*Value, env *Environment) (*Value, error) {
	if len(args) != 2 {
		return nil, fmt.Errorf("char-ci>=? expects exactly 2 arguments")
	}

	if args[0].Type != TypeChar || args[1].Type != TypeChar {
		return nil, fmt.Errorf("char-ci>=? expects characters")
	}

	return NewBoolean(unicode.ToLower(args[0].Char) >= unicode.ToLower(args[1].Char)), nil
}

// builtinCharAlphabeticp implements char-alphabetic?
func builtinCharAlphabeticp(args []*Value, env *Environment) (*Value, error) {
	if len(args) != 1 {
		return nil, fmt.Errorf("char-alphabetic? expects exactly 1 argument")
	}

	if args[0].Type != TypeChar {
		return nil, fmt.Errorf("char-alphabetic? expects a character")
	}

	return NewBoolean(unicode.IsLetter(args[0].Char)), nil
}

// builtinCharNumericp implements char-numeric?
func builtinCharNumericp(args []*Value, env *Environment) (*Value, error) {
	if len(args) != 1 {
		return nil, fmt.Errorf("char-numeric? expects exactly 1 argument")
	}

	if args[0].Type != TypeChar {
		return nil, fmt.Errorf("char-numeric? expects a character")
	}

	return NewBoolean(unicode.IsDigit(args[0].Char)), nil
}

// builtinCharWhitespacep implements char-whitespace?
func builtinCharWhitespacep(args []*Value, env *Environment) (*Value, error) {
	if len(args) != 1 {
		return nil, fmt.Errorf("char-whitespace? expects exactly 1 argument")
	}

	if args[0].Type != TypeChar {
		return nil, fmt.Errorf("char-whitespace? expects a character")
	}

	return NewBoolean(unicode.IsSpace(args[0].Char)), nil
}

// builtinCharUpperCasep implements char-upper-case?
func builtinCharUpperCasep(args []*Value, env *Environment) (*Value, error) {
	if len(args) != 1 {
		return nil, fmt.Errorf("char-upper-case? expects exactly 1 argument")
	}

	if args[0].Type != TypeChar {
		return nil, fmt.Errorf("char-upper-case? expects a character")
	}

	return NewBoolean(unicode.IsUpper(args[0].Char)), nil
}

// builtinCharLowerCasep implements char-lower-case?
func builtinCharLowerCasep(args []*Value, env *Environment) (*Value, error) {
	if len(args) != 1 {
		return nil, fmt.Errorf("char-lower-case? expects exactly 1 argument")
	}

	if args[0].Type != TypeChar {
		return nil, fmt.Errorf("char-lower-case? expects a character")
	}

	return NewBoolean(unicode.IsLower(args[0].Char)), nil
}

// builtinCharToInteger implements char->integer
func builtinCharToInteger(args []*Value, env *Environment) (*Value, error) {
	if len(args) != 1 {
		return nil, fmt.Errorf("char->integer expects exactly 1 argument")
	}

	if args[0].Type != TypeChar {
		return nil, fmt.Errorf("char->integer expects a character")
	}

	return NewInteger(int64(args[0].Char)), nil
}

// builtinIntegerToChar implements integer->char
func builtinIntegerToChar(args []*Value, env *Environment) (*Value, error) {
	if len(args) != 1 {
		return nil, fmt.Errorf("integer->char expects exactly 1 argument")
	}

	if !args[0].IsNumber() {
		return nil, fmt.Errorf("integer->char expects an integer")
	}

	// Convert to integer
	intVal := args[0].ToInteger()
	if intVal == nil {
		return nil, fmt.Errorf("integer->char expects an exact integer")
	}

	// Check if it's a valid character code
	code := intVal.Int64()
	if code < 0 || code > unicode.MaxRune {
		return nil, fmt.Errorf("integer->char: argument out of range")
	}

	return NewChar(rune(code)), nil
}

// builtinCharUpcase implements char-upcase
func builtinCharUpcase(args []*Value, env *Environment) (*Value, error) {
	if len(args) != 1 {
		return nil, fmt.Errorf("char-upcase expects exactly 1 argument")
	}

	if args[0].Type != TypeChar {
		return nil, fmt.Errorf("char-upcase expects a character")
	}

	return NewChar(unicode.ToUpper(args[0].Char)), nil
}

// builtinCharDowncase implements char-downcase
func builtinCharDowncase(args []*Value, env *Environment) (*Value, error) {
	if len(args) != 1 {
		return nil, fmt.Errorf("char-downcase expects exactly 1 argument")
	}

	if args[0].Type != TypeChar {
		return nil, fmt.Errorf("char-downcase expects a character")
	}

	return NewChar(unicode.ToLower(args[0].Char)), nil
}

// builtinProcedurep implements procedure?
// (procedure? obj) returns #t if obj is a procedure, #f otherwise
func builtinProcedurep(args []*Value, env *Environment) (*Value, error) {
	if len(args) != 1 {
		return nil, fmt.Errorf("procedure? expects exactly 1 argument")
	}

	obj := args[0]
	// A procedure is either a built-in function, a user-defined function (lambda), or a built-in special form
	switch obj.Type {
	case TypeBuiltin:
		return NewBoolean(true), nil
	case TypeFunction:
		return NewBoolean(true), nil
	default:
		return NewBoolean(false), nil
	}
}

// builtinApply implements apply
// (apply proc args) applies proc to the arguments in the list args
func builtinApply(args []*Value, env *Environment) (*Value, error) {
	if len(args) != 2 {
		return nil, fmt.Errorf("apply expects exactly 2 arguments")
	}

	proc := args[0]
	argList := args[1]

	// Check that the first argument is a procedure
	if proc.Type != TypeBuiltin && proc.Type != TypeFunction {
		return nil, fmt.Errorf("apply: first argument must be a procedure")
	}

	// Check that the second argument is a list
	if argList.Type != TypeList && argList.Type != TypeNil {
		return nil, fmt.Errorf("apply: second argument must be a list")
	}

	// Convert the argument list to a slice
	var arguments []*Value
	if argList.Type == TypeList {
		arguments = argList.List
	} else {
		// Empty list
		arguments = []*Value{}
	}

	// Apply the procedure to the arguments
	if proc.Type == TypeBuiltin {
		return proc.Builtin(arguments, env)
	} else {
		// TypeFunction - user-defined function
		fn := proc.Function

		// Check parameter count
		if len(arguments) != len(fn.Parameters) {
			return nil, fmt.Errorf("apply: function expects %d arguments, got %d", len(fn.Parameters), len(arguments))
		}

		// Create new environment for function execution
		funcEnv := NewEnvironment(fn.Closure)

		// Bind parameters to arguments
		for i, param := range fn.Parameters {
			funcEnv.Define(param, arguments[i])
		}

		// Evaluate function body in new environment
		return Eval(fn.Body, funcEnv)
	}
}

// builtinForEach implements for-each
// (for-each proc list1 list2 ...) applies proc to corresponding elements of lists and returns unspecified
func builtinForEach(args []*Value, env *Environment) (*Value, error) {
	if len(args) < 2 {
		return nil, fmt.Errorf("for-each expects at least 2 arguments (procedure and one list)")
	}

	proc := args[0]
	lists := args[1:]

	// Check that the first argument is a procedure
	if proc.Type != TypeBuiltin && proc.Type != TypeFunction {
		return nil, fmt.Errorf("for-each: first argument must be a procedure")
	}

	// Check that all remaining arguments are lists or vectors or nil
	for i, arg := range lists {
		if arg.Type != TypeList && arg.Type != TypeVector && arg.Type != TypeNil {
			return nil, fmt.Errorf("for-each: argument %d must be a list, vector or nil", i+2)
		}
	}

	// Find the minimum length among all lists and vectors
	minLength := -1
	for _, arg := range lists {
		var length int
		switch arg.Type {
		case TypeNil:
			length = 0
		case TypeList:
			length = len(arg.List)
		case TypeVector:
			length = len(arg.Vector)
		}

		if minLength == -1 || length < minLength {
			minLength = length
		}
	}

	// Apply procedure to corresponding elements
	for i := 0; i < minLength; i++ {
		// Collect arguments for this iteration
		procArgs := make([]*Value, len(lists))
		for j, arg := range lists {
			if arg.Type == TypeNil {
				// This shouldn't happen since we checked minLength
				return nil, fmt.Errorf("for-each: internal error - empty list")
			}
			if arg.Type == TypeList {
				procArgs[j] = arg.List[i]
			} else if arg.Type == TypeVector {
				procArgs[j] = arg.Vector[i]
			}
		}

		// Apply procedure to arguments
		var err error
		if proc.Type == TypeBuiltin {
			_, err = proc.Builtin(procArgs, env)
		} else {
			// TypeFunction - user-defined function
			fn := proc.Function

			// Check parameter count
			if len(procArgs) != len(fn.Parameters) {
				return nil, fmt.Errorf("for-each: function expects %d arguments, got %d", len(fn.Parameters), len(procArgs))
			}

			// Create new environment for function execution
			funcEnv := NewEnvironment(fn.Closure)

			// Bind parameters to arguments
			for k, param := range fn.Parameters {
				funcEnv.Define(param, procArgs[k])
			}

			// Evaluate function body in new environment
			_, err = Eval(fn.Body, funcEnv)
		}

		if err != nil {
			return nil, fmt.Errorf("for-each: error applying procedure: %v", err)
		}
	}

	// Return an unspecified value (nil in this implementation)
	return NewNil(), nil
}

// builtinMap implements map
// (map proc list1 list2 ...) applies proc to corresponding elements of lists
func builtinMap(args []*Value, env *Environment) (*Value, error) {
	if len(args) < 2 {
		return nil, fmt.Errorf("map expects at least 2 arguments (procedure and one list)")
	}

	proc := args[0]
	lists := args[1:]

	// Check that the first argument is a procedure
	if proc.Type != TypeBuiltin && proc.Type != TypeFunction {
		return nil, fmt.Errorf("map: first argument must be a procedure")
	}

	// Check that all remaining arguments are lists
	for i, list := range lists {
		if list.Type != TypeList && list.Type != TypeNil {
			return nil, fmt.Errorf("map: argument %d must be a list", i+2)
		}
	}

	// Find the minimum length among all lists
	minLength := -1
	for _, list := range lists {
		var length int
		if list.Type == TypeNil {
			length = 0
		} else {
			length = len(list.List)
		}

		if minLength == -1 || length < minLength {
			minLength = length
		}
	}

	// If any list is empty, return empty list
	if minLength == 0 {
		return NewList([]*Value{}), nil
	}

	// Apply procedure to corresponding elements
	results := make([]*Value, minLength)
	for i := 0; i < minLength; i++ {
		// Collect arguments for this iteration
		procArgs := make([]*Value, len(lists))
		for j, list := range lists {
			if list.Type == TypeNil {
				// This shouldn't happen since we checked minLength
				return nil, fmt.Errorf("map: internal error - empty list")
			}
			procArgs[j] = list.List[i]
		}

		// Apply procedure to arguments
		var result *Value
		var err error
		if proc.Type == TypeBuiltin {
			result, err = proc.Builtin(procArgs, env)
		} else {
			// TypeFunction - user-defined function
			fn := proc.Function

			// Check parameter count
			if len(procArgs) != len(fn.Parameters) {
				return nil, fmt.Errorf("map: function expects %d arguments, got %d", len(fn.Parameters), len(procArgs))
			}

			// Create new environment for function execution
			funcEnv := NewEnvironment(fn.Closure)

			// Bind parameters to arguments
			for k, param := range fn.Parameters {
				funcEnv.Define(param, procArgs[k])
			}

			// Evaluate function body in new environment
			result, err = Eval(fn.Body, funcEnv)
		}

		if err != nil {
			return nil, fmt.Errorf("map: error applying procedure: %v", err)
		}

		results[i] = result
	}

	return NewList(results), nil
}

// eofObject is the representation of the end-of-file object in Scheme
var eofObject = &Value{Type: TypeEOF}

// builtinEofObjectp implements eof-object?
// (eof-object? obj) returns #t if obj is the end-of-file object, #f otherwise
func builtinEofObjectp(args []*Value, env *Environment) (*Value, error) {
	if len(args) != 1 {
		return nil, fmt.Errorf("eof-object? expects exactly 1 argument")
	}
	return NewBoolean(args[0] == eofObject), nil
}

// builtinReadChar implements read-char
// (read-char) reads a single character from standard input
func builtinReadChar(args []*Value, env *Environment) (*Value, error) {
	if len(args) != 0 {
		return nil, fmt.Errorf("read-char expects exactly 0 arguments")
	}

	reader := bufio.NewReader(os.Stdin)
	ch, _, err := reader.ReadRune()
	if err != nil {
		// Return eof-object on EOF
		return eofObject, nil
	}

	return NewChar(ch), nil
}

// builtinWriteChar implements write-char
// (write-char char) writes a single character to standard output
func builtinWriteChar(args []*Value, env *Environment) (*Value, error) {
	if len(args) != 1 {
		return nil, fmt.Errorf("write-char expects exactly 1 argument")
	}

	arg := args[0]
	if arg.Type != TypeChar {
		return nil, fmt.Errorf("write-char expects a character argument")
	}

	fmt.Printf("%c", arg.Char)
	// According to R4RS, write-char returns an unspecified value
	return NewNil(), nil
}

// builtinRead implements read
// (read) reads a Scheme expression from standard input
func builtinRead(args []*Value, env *Environment) (*Value, error) {
	if len(args) != 0 {
		return nil, fmt.Errorf("read expects exactly 0 arguments")
	}

	// Read from stdin until we get a complete expression
	reader := bufio.NewReader(os.Stdin)
	var lines []string

	for {
		// Read a line from input
		input, err := reader.ReadString('\n')
		if err != nil {
			return nil, fmt.Errorf("read: error reading input: %v", err)
		}

		// Remove trailing newline
		input = strings.TrimSuffix(input, "\n")
		lines = append(lines, input)

		// Combine all lines so far
		combined := strings.Join(lines, " ")
		trimmed := strings.TrimSpace(combined)

		// Check if we have a complete expression
		if trimmed == "" {
			continue
		}

		// Count parentheses balance
		parenBalance := countParens(trimmed)
		if parenBalance == 0 {
			// We have a complete expression, parse it
			tokens, err := Tokenize(trimmed)
			if err != nil {
				return nil, fmt.Errorf("read: error tokenizing input: %v", err)
			}

			parser := NewParser(tokens)
			expr, err := parser.parseExpression()
			if err != nil {
				return nil, fmt.Errorf("read: error parsing input: %v", err)
			}

			return expr, nil
		}
		// If not balanced, continue reading more lines
	}
}

// builtinNewline implements newline
// (newline) outputs a newline character
func builtinNewline(args []*Value, env *Environment) (*Value, error) {
	if len(args) != 0 {
		return nil, fmt.Errorf("newline expects no arguments")
	}
	fmt.Println()
	// Returns an unspecified value
	return NewNil(), nil
}

// builtinWrite implements write
// (write obj) writes obj to standard output in a way that can be read back
func builtinWrite(args []*Value, env *Environment) (*Value, error) {
	if len(args) != 1 {
		return nil, fmt.Errorf("write expects exactly 1 argument")
	}

	obj := args[0]

	// Write the object using its String representation
	// This provides a representation that can be read back by the reader
	fmt.Print(obj.String())

	// According to R4RS, write returns an unspecified value
	// We'll return the object itself for simplicity
	return obj, nil
}

// builtinDisplay implements display
// (display obj) writes obj to standard output in a human-readable form
func builtinDisplay(args []*Value, env *Environment) (*Value, error) {
	if len(args) != 1 {
		return nil, fmt.Errorf("display expects exactly 1 argument")
	}

	obj := args[0]

	// Display the object in human-readable form
	// For most types, this is the same as String(), but for strings we don't show quotes
	switch obj.Type {
	case TypeString:
		// Display strings without quotes for human readability
		fmt.Print(obj.StringVal)
	default:
		// For all other types, use the standard string representation
		fmt.Print(obj.String())
	}

	// According to R4RS, display returns an unspecified value
	// We'll return the object itself for simplicity
	return obj, nil
}

// Helper function to count parentheses balance (moved from main.go for reuse)
func countParens(s string) int {
	count := 0
	inString := false
	escaped := false
	inComment := false

	for _, char := range s {
		if escaped {
			escaped = false
			continue
		}

		// Handle comments - skip everything after semicolon until newline
		if char == ';' && !inString {
			inComment = true
			continue
		}

		if char == '\n' {
			inComment = false
			continue
		}

		// Skip everything when in comment
		if inComment {
			continue
		}

		if char == '\\' && inString {
			escaped = true
			continue
		}

		if char == '"' {
			inString = !inString
			continue
		}

		if !inString {
			if char == '(' {
				count++
			} else if char == ')' {
				count--
			}
		}
	}

	return count
}

// Complex number procedures

// builtinComplexp implements the complex? predicate
func builtinComplexp(args []*Value, env *Environment) (*Value, error) {
	if len(args) != 1 {
		return nil, fmt.Errorf("complex? expects exactly 1 argument")
	}

	// In R4RS, all numbers are complex numbers
	// Integer, rational, real, and complex are all complex
	return NewBoolean(args[0].IsNumber()), nil
}

// builtinRealp implements the real? predicate
func builtinRealp(args []*Value, env *Environment) (*Value, error) {
	if len(args) != 1 {
		return nil, fmt.Errorf("real? expects exactly 1 argument")
	}

	arg := args[0]
	if !arg.IsNumber() {
		return NewBoolean(false), nil
	}

	// Real numbers: integers, rationals, reals, and complex with zero imaginary part
	if arg.Type == TypeComplex {
		return NewBoolean(imag(arg.Complex) == 0), nil
	}
	return NewBoolean(true), nil
}

// builtinRationalp implements the rational? predicate
func builtinRationalp(args []*Value, env *Environment) (*Value, error) {
	if len(args) != 1 {
		return nil, fmt.Errorf("rational? expects exactly 1 argument")
	}

	arg := args[0]
	if !arg.IsNumber() {
		return NewBoolean(false), nil
	}

	// Rational numbers: integers, rationals, and complex numbers with zero imaginary part
	// that can be expressed as rationals
	if arg.Type == TypeComplex {
		if imag(arg.Complex) != 0 {
			return NewBoolean(false), nil
		}
		// Check if the real part is rational
		realPart := real(arg.Complex)
		return NewBoolean(realPart == float64(int64(realPart))), nil
	}
	return NewBoolean(arg.Type != TypeReal || (arg.Type == TypeReal && arg.Real == float64(int64(arg.Real)))), nil
}

// builtinIntegerp implements the integer? predicate
func builtinIntegerp(args []*Value, env *Environment) (*Value, error) {
	if len(args) != 1 {
		return nil, fmt.Errorf("integer? expects exactly 1 argument")
	}

	arg := args[0]
	if !arg.IsNumber() {
		return NewBoolean(false), nil
	}

	// Integer numbers: integers and complex numbers with zero imaginary part
	// that are whole numbers
	if arg.Type == TypeComplex {
		if imag(arg.Complex) != 0 {
			return NewBoolean(false), nil
		}
		realPart := real(arg.Complex)
		return NewBoolean(realPart == float64(int64(realPart))), nil
	}
	if arg.Type == TypeReal {
		return NewBoolean(arg.Real == float64(int64(arg.Real))), nil
	}
	return NewBoolean(arg.Type == TypeInteger), nil
}

// builtinMakeRectangular implements the make-rectangular procedure
// (make-rectangular real imag) creates a complex number from real and imaginary parts
func builtinMakeRectangular(args []*Value, env *Environment) (*Value, error) {
	if len(args) != 2 {
		return nil, fmt.Errorf("make-rectangular expects exactly 2 arguments")
	}

	if !args[0].IsNumber() || !args[1].IsNumber() {
		return nil, fmt.Errorf("make-rectangular expects number arguments")
	}

	realPart := args[0].ToFloat()
	imagPart := args[1].ToFloat()

	// Determine exactness: exact if both parts are exact
	exact := args[0].Exact && args[1].Exact

	return NewComplex(complex(realPart, imagPart), exact), nil
}

// builtinMakePolar implements the make-polar procedure
// (make-polar magnitude angle) creates a complex number from polar coordinates
func builtinMakePolar(args []*Value, env *Environment) (*Value, error) {
	if len(args) != 2 {
		return nil, fmt.Errorf("make-polar expects exactly 2 arguments")
	}

	if !args[0].IsNumber() || !args[1].IsNumber() {
		return nil, fmt.Errorf("make-polar expects number arguments")
	}

	magnitude := args[0].ToFloat()
	angle := args[1].ToFloat()

	// Determine exactness: exact if both parts are exact
	exact := args[0].Exact && args[1].Exact

	return NewPolar(magnitude, angle, exact), nil
}

// builtinRealPart implements the real-part procedure
// (real-part z) returns the real part of complex number z
func builtinRealPart(args []*Value, env *Environment) (*Value, error) {
	if len(args) != 1 {
		return nil, fmt.Errorf("real-part expects exactly 1 argument")
	}

	if !args[0].IsNumber() {
		return nil, fmt.Errorf("real-part expects a number argument")
	}

	arg := args[0]
	switch arg.Type {
	case TypeInteger:
		return NewIntegerBig(new(big.Int).Set(arg.Integer)), nil
	case TypeRational:
		return NewRationalBig(new(big.Rat).Set(arg.Rational)), nil
	case TypeReal:
		return NewReal(arg.Real, arg.Exact), nil
	case TypeComplex:
		realPart := real(arg.Complex)
		return NewReal(realPart, arg.Exact), nil
	default:
		return nil, fmt.Errorf("real-part: invalid number type")
	}
}

// builtinImagPart implements the imag-part procedure
// (imag-part z) returns the imaginary part of complex number z
func builtinImagPart(args []*Value, env *Environment) (*Value, error) {
	if len(args) != 1 {
		return nil, fmt.Errorf("imag-part expects exactly 1 argument")
	}

	if !args[0].IsNumber() {
		return nil, fmt.Errorf("imag-part expects a number argument")
	}

	arg := args[0]
	switch arg.Type {
	case TypeInteger, TypeRational, TypeReal:
		// Real numbers have zero imaginary part
		return NewInteger(0), nil
	case TypeComplex:
		imagPart := imag(arg.Complex)
		return NewReal(imagPart, arg.Exact), nil
	default:
		return nil, fmt.Errorf("imag-part: invalid number type")
	}
}

// builtinMagnitude implements the magnitude procedure
// (magnitude z) returns the magnitude (absolute value) of complex number z
func builtinMagnitude(args []*Value, env *Environment) (*Value, error) {
	if len(args) != 1 {
		return nil, fmt.Errorf("magnitude expects exactly 1 argument")
	}

	if !args[0].IsNumber() {
		return nil, fmt.Errorf("magnitude expects a number argument")
	}

	arg := args[0]
	switch arg.Type {
	case TypeInteger:
		// For integers, magnitude is absolute value
		result := new(big.Int).Abs(arg.Integer)
		return NewIntegerBig(result), nil
	case TypeRational:
		// For rationals, magnitude is absolute value
		result := new(big.Rat).Abs(arg.Rational)
		return NewRationalBig(result), nil
	case TypeReal:
		// For reals, magnitude is absolute value
		result := math.Abs(arg.Real)
		return NewReal(result, arg.Exact), nil
	case TypeComplex:
		// For complex numbers, magnitude is sqrt(real^2 + imag^2)
		real := real(arg.Complex)
		imag := imag(arg.Complex)
		result := math.Sqrt(real*real + imag*imag)
		return NewReal(result, arg.Exact), nil
	default:
		return nil, fmt.Errorf("magnitude: invalid number type")
	}
}

// builtinAngle implements the angle procedure
// (angle z) returns the angle (argument) of complex number z
func builtinAngle(args []*Value, env *Environment) (*Value, error) {
	if len(args) != 1 {
		return nil, fmt.Errorf("angle expects exactly 1 argument")
	}

	if !args[0].IsNumber() {
		return nil, fmt.Errorf("angle expects a number argument")
	}

	arg := args[0]
	switch arg.Type {
	case TypeInteger, TypeRational, TypeReal:
		// Real numbers: angle is 0 for positive, π for negative
		floatVal := arg.ToFloat()
		if floatVal >= 0 {
			return NewReal(0.0, false), nil // Angle of positive real is 0
		} else {
			return NewReal(math.Pi, false), nil // Angle of negative real is π
		}
	case TypeComplex:
		// For complex numbers, angle is atan2(imag, real)
		realPart := real(arg.Complex)
		imagPart := imag(arg.Complex)
		result := math.Atan2(imagPart, realPart)
		return NewReal(result, false), nil // Angles are typically inexact
	default:
		return nil, fmt.Errorf("angle: invalid number type")
	}
}

// Vector procedures (R4RS section 6.6)
func builtinVectorp(args []*Value, env *Environment) (*Value, error) {
	if len(args) != 1 {
		return nil, fmt.Errorf("vector? expects exactly 1 argument")
	}
	return NewBoolean(args[0].Type == TypeVector), nil
}

func builtinMakeVector(args []*Value, env *Environment) (*Value, error) {
	if len(args) < 1 || len(args) > 2 {
		return nil, fmt.Errorf("make-vector expects 1 or 2 arguments")
	}

	// Get the length
	length, err := toNonNegativeInteger(args[0])
	if err != nil {
		return nil, err
	}

	// Create the vector
	vector := make([]*Value, length)

	// If initial value is provided, fill the vector
	if len(args) == 2 {
		fillVal := args[1]
		for i := 0; i < length; i++ {
			vector[i] = fillVal
		}
	} else {
		// Default fill with nil
		for i := 0; i < length; i++ {
			vector[i] = NewNil()
		}
	}

	return NewVector(vector), nil
}

func builtinVector(args []*Value, env *Environment) (*Value, error) {
	// Create a vector from the arguments
	vector := make([]*Value, len(args))
	for i, arg := range args {
		vector[i] = arg
	}
	return NewVector(vector), nil
}

func builtinVectorLength(args []*Value, env *Environment) (*Value, error) {
	if len(args) != 1 {
		return nil, fmt.Errorf("vector-length expects exactly 1 argument")
	}

	if args[0].Type != TypeVector {
		return nil, fmt.Errorf("vector-length expects a vector")
	}

	return NewInteger(int64(len(args[0].Vector))), nil
}

func builtinVectorRef(args []*Value, env *Environment) (*Value, error) {
	if len(args) != 2 {
		return nil, fmt.Errorf("vector-ref expects exactly 2 arguments")
	}

	if args[0].Type != TypeVector {
		return nil, fmt.Errorf("vector-ref expects a vector as first argument")
	}

	index, err := toNonNegativeInteger(args[1])
	if err != nil {
		return nil, err
	}

	vector := args[0].Vector
	if index < 0 || index >= len(vector) {
		return nil, fmt.Errorf("vector-ref index out of bounds: %d, length: %d", index, len(vector))
	}

	return vector[index], nil
}

func builtinVectorSet(args []*Value, env *Environment) (*Value, error) {
	if len(args) != 3 {
		return nil, fmt.Errorf("vector-set! expects exactly 3 arguments")
	}

	if args[0].Type != TypeVector {
		return nil, fmt.Errorf("vector-set! expects a vector as first argument")
	}

	index, err := toNonNegativeInteger(args[1])
	if err != nil {
		return nil, err
	}

	vector := args[0].Vector
	if index < 0 || index >= len(vector) {
		return nil, fmt.Errorf("vector-set! index out of bounds: %d, length: %d", index, len(vector))
	}

	// Set the value
	vector[index] = args[2]
	return NewVoid(), nil
}

func builtinVectorToList(args []*Value, env *Environment) (*Value, error) {
	if len(args) != 1 {
		return nil, fmt.Errorf("vector->list expects exactly 1 argument")
	}

	if args[0].Type != TypeVector {
		return nil, fmt.Errorf("vector->list expects a vector")
	}

	vector := args[0].Vector
	if len(vector) == 0 {
		return NewNil(), nil
	}

	// Create a list from the vector elements
	result := NewNil()
	for i := len(vector) - 1; i >= 0; i-- {
		result = NewPair(vector[i], result)
	}

	return result, nil
}

func builtinListToVector(args []*Value, env *Environment) (*Value, error) {
	if len(args) != 1 {
		return nil, fmt.Errorf("list->vector expects exactly 1 argument")
	}

	list := args[0]
	var elements []*Value

	// Handle empty list case
	if list.Type == TypeNil || (list.Type == TypeList && len(list.List) == 0) {
		return NewVector(elements), nil
	}

	// Handle proper list built with List type
	if list.Type == TypeList {
		return NewVector(list.List), nil
	}

	// Collect elements from pair-based list
	current := list
	for current.Type != TypeNil {
		if current.Type != TypePair {
			return nil, fmt.Errorf("list->vector expects a proper list")
		}
		// Add the car to elements
		elements = append(elements, current.Car)
		// Move to next pair
		current = current.Cdr
	}

	// Create a vector from the collected elements
	return NewVector(elements), nil
}

func builtinVectorFill(args []*Value, env *Environment) (*Value, error) {
	if len(args) != 2 {
		return nil, fmt.Errorf("vector-fill! expects exactly 2 arguments")
	}

	if args[0].Type != TypeVector {
		return nil, fmt.Errorf("vector-fill! expects a vector as first argument")
	}

	fillVal := args[1]
	vector := args[0].Vector

	// Fill the vector with the value
	for i := range vector {
		vector[i] = fillVal
	}

	return NewVoid(), nil
}

// Helper function to convert a value to a non-negative integer
func toNonNegativeInteger(val *Value) (int, error) {
	if !val.IsNumber() {
		return 0, fmt.Errorf("expected a number")
	}

	// Convert to integer
	integer := val.ToInteger()
	if integer == nil {
		return 0, fmt.Errorf("expected an integer")
	}

	// Check if non-negative
	if integer.Sign() < 0 {
		return 0, fmt.Errorf("expected a non-negative integer")
	}

	// Check if it's within int range
	if integer.IsInt64() {
		intVal := integer.Int64()
		if intVal > int64(^uint(0)>>1) {
			return 0, fmt.Errorf("integer too large")
		}
		return int(intVal), nil
	}

	return 0, fmt.Errorf("integer too large")
}

// Represents a continuation value that will be used with panic/recover
// to implement non-local control flow
var continuationKey = "__continuation_key__"

// Continuation is a type that wraps a value to be used with panic/recover
// to implement non-local control flow for call-with-current-continuation
type Continuation struct {
	Value *Value
	IsContinuation bool
}

// MakeContinuation creates a new continuation value
func MakeContinuation(value *Value) *Value {
	return &Value{
		Type: TypeContinuation,
		Continuation: value,
	}
}

// builtinCallWithCurrentContinuation implements the call-with-current-continuation
// procedure from Scheme. It captures the current evaluation context and passes
// a continuation procedure to the given argument procedure.
func builtinCallWithCurrentContinuation(args []*Value, env *Environment) (*Value, error) {
	if len(args) != 1 {
		return nil, fmt.Errorf("call-with-current-continuation expects exactly 1 argument")
	}

	// The argument should be a procedure
	proc := args[0]
	if proc.Type != TypeFunction && proc.Type != TypeBuiltin {
		return nil, fmt.Errorf("call-with-current-continuation expects a procedure")
	}

	// 定义continuation过程，当被调用时，会非本地地跳转到当前点
	continuationProc := NewBuiltin(func(contArgs []*Value, contEnv *Environment) (*Value, error) {
		if len(contArgs) != 1 {
			return nil, fmt.Errorf("continuation expects exactly 1 argument")
		}
		fmt.Println("DEBUG: 在continuation函数中，参数值:", contArgs[0])
		// 当continuation被调用时，使用panic跳回call-with-current-continuation
		// 此时，continuation的参数将成为call/cc表达式的值
		panic(Continuation{Value: contArgs[0], IsContinuation: true})
		return nil, nil // 这一行不可达，但为了Go语法要求
	})

	fmt.Println("DEBUG: 调用传入的过程")
	// 直接调用过程，让continuation panic被更高级别的recover处理
	// 这里不需要本地的defer/recover，因为我们希望continuation能够完全控制执行流程
	return Apply(proc, []*Value{continuationProc}, env)
}

// Apply applies a procedure to arguments. This is a helper function that
// handles both user-defined functions and built-in functions.
func Apply(proc *Value, args []*Value, env *Environment) (*Value, error) {
	switch proc.Type {
	case TypeBuiltin:
		return proc.Builtin(args, env)
	case TypeFunction:
		if len(args) != len(proc.Function.Parameters) {
			return nil, fmt.Errorf("function expects %d arguments, got %d",
				len(proc.Function.Parameters), len(args))
		}
		// Create new environment for function scope
		fnEnv := NewEnvironment(proc.Function.Closure)
		// Bind parameters to arguments
		for i, param := range proc.Function.Parameters {
			fnEnv.Define(param, args[i])
		}
		// Evaluate function body with tail call optimization
		return EvalWithTCO(proc.Function.Body, fnEnv)
	default:
		return nil, fmt.Errorf("cannot apply %v as function", proc.Type)
	}
}
